In this notebook we are comparing the use of Alevin-fry and Spaceranger for quantifying spatial transcriptomics libraries. Two spatial transcriptomic libraries were quantified using Alevin-fry and Spaceranger and the results were combined following the Alevin-fry tutorial. We are comparing that to if we were to not integrate the Spaceranger data with Alevin-fry and only use Spaceranger. When performing this analysis all tools used an index with Ensembl 104. Here we will look at two libraries, SCPCR000372 and SCPCR000373. Alevin-fry was run both using the knee filtering and the unfiltered permit list mode. Included here is also use of Alevin-fry with the --sketch alignment mode and unfiltered permit list.

Note that SpatialExperiment was installed from Github, in order to reflect the most recent changes in read10XVisium at commit ddb15e0.

Set Up

library(magrittr)
library(ggplot2)
library(SingleCellExperiment)
library(SpatialExperiment)
library(ggupset)
library(gridExtra)

Attaching package: ‘gridExtra’

The following object is masked from ‘package:Biobase’:

    combine

The following object is masked from ‘package:BiocGenerics’:

    combine
library(ggrepel)
library(clusterProfiler)

Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
clusterProfiler v4.2.0  For help: https://yulab-smu.top/biomedical-knowledge-mining-book/

If you use clusterProfiler in published research, please cite:
T Wu, E Hu, S Xu, M Chen, P Guo, Z Dai, T Feng, L Zhou, W Tang, L Zhan, X Fu, S Liu, X Bo, and G Yu. clusterProfiler 4.0: A universal enrichment tool for interpreting omics data. The Innovation. 2021, 2(3):100141

Attaching package: ‘clusterProfiler’

The following object is masked from ‘package:IRanges’:

    slice

The following object is masked from ‘package:S4Vectors’:

    rename

The following object is masked from ‘package:stats’:

    filter
library(org.Hs.eg.db)
Loading required package: AnnotationDbi
package ‘AnnotationDbi’ was built under R version 4.1.2
Attaching package: ‘AnnotationDbi’

The following object is masked from ‘package:clusterProfiler’:

    select
# set seed for ORA
set.seed(2021)
# load in benchmarking functions that will be used for copying data and generating sample tables
function_path <- file.path(".." ,"benchmarking-functions", "R")
file.path(function_path, list.files(function_path, pattern = "*.R$")) %>%
  purrr::walk(source)
# set up file paths 
base_dir <- here::here()

# folder with alevin-fry and cellranger quants from S3
data_dir <- file.path(base_dir, "data", "spatial") 
quants_dir <- file.path(data_dir, "data", "quants")

# results directory 
results_dir <- file.path(data_dir, "results")

# sample name
sample_ids <- c("SCPCR000372", "SCPCR000373")
mito_file <- file.path(base_dir, "sample-info", "Homo_sapiens.GRCh38.104.mitogenes.txt")
  
# read in mito genes 
mito_genes <- readr::read_tsv(mito_file, col_names = "gene_id")
<<<<<<< HEAD
Rows: 111 Columns: 1
── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
=======

Rows: 37 Columns: 1
── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────
>>>>>>> benchmarking-spatial-fry-unfiltered
Delimiter: "\t"
chr (1): gene_id

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
mito_genes <- mito_genes %>%
  dplyr::pull(gene_id) %>%
  unique()

Copy from S3

# download alevin fry and cellranger output
aws_copy_samples(local_dir = quants_dir,
                 s3_dir = "s3://nextflow-ccdl-results/scpca",
                 samples = sample_ids,
                 tools = c("alevin-fry-knee", "alevin-fry-unfiltered", "cellranger"))

Alevin-fry + Spaceranger versus Spaceranger Only

Now let’s take a look at comparing the two methods of using Alevin-fry + Spaceranger to only Spaceranger for quantification. To do this, we will read in the Alevin-fry + Spaceranger combined and Spaceranger only SpatialExperiment objects separately and then merge them into one list before grabbing the per cell and per gene quality metrics.

Create Spatial Experiments

# get path to fry knee output directory 
fry_knee_dir <- file.path(quants_dir, "alevin-fry-knee", sample_ids)
fry_knee_dir <- paste0(fry_knee_dir, "-Homo_sapiens.GRCh38.104.spliced_intron.txome-salign-cr-like-em-knee")

# get path to fry unfiltered output directory 
fry_unfiltered_dir <- file.path(quants_dir, "alevin-fry-unfiltered", sample_ids)
fry_unfiltered_dir <- paste0(fry_unfiltered_dir, "-Homo_sapiens.GRCh38.104.spliced_intron.txome-salign-cr-like-em")

# get path to fry unfiltered sketch output directory 
fry_sketch_dir <- file.path(quants_dir, "alevin-fry-unfiltered", sample_ids)
fry_sketch_dir <- paste0(fry_sketch_dir, "-Homo_sapiens.GRCh38.104.spliced_intron.txome-sketch-cr-like-em")

# paths to spatial folders 
cellranger_folders <- paste0(sample_ids, "-GRCh38_104_cellranger_full-spatial")
spaceranger_dir <- file.path(quants_dir, "cellranger", cellranger_folders)
# read in combined fry and spaceranger spe for fry knee
fry_knee_spe_1 <- create_fry_spaceranger_spe(fry_knee_dir[1], 
                                             spaceranger_dir[1], 
                                             sample_ids[1])
<<<<<<< HEAD
Rows: 4992 Columns: 6
── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
=======

Rows: 4992 Columns: 6
── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────
>>>>>>> benchmarking-spatial-fry-unfiltered
Delimiter: ","
chr (1): barcode
dbl (5): in_tissue, array_row, array_col, pxl_row_in_fullres, pxl_col_in_fullres

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Joining, by = "barcode"
fry_knee_spe_2 <- create_fry_spaceranger_spe(fry_knee_dir[2], 
                                             spaceranger_dir[2], 
                                             sample_ids[2])
<<<<<<< HEAD
Rows: 4992 Columns: 6
── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
=======

Rows: 4992 Columns: 6
── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────
>>>>>>> benchmarking-spatial-fry-unfiltered
Delimiter: ","
chr (1): barcode
dbl (5): in_tissue, array_row, array_col, pxl_row_in_fullres, pxl_col_in_fullres

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Joining, by = "barcode"
# read in combined fry and spaceranger spe for fry unfiltered
fry_unfiltered_spe_1 <- create_fry_spaceranger_spe(fry_unfiltered_dir[1], 
                                                   spaceranger_dir[1], 
                                                   sample_ids[1])
<<<<<<< HEAD
Rows: 4992 Columns: 6
── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
=======

Rows: 4992 Columns: 6
── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────
>>>>>>> benchmarking-spatial-fry-unfiltered
Delimiter: ","
chr (1): barcode
dbl (5): in_tissue, array_row, array_col, pxl_row_in_fullres, pxl_col_in_fullres

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Joining, by = "barcode"
fry_unfiltered_spe_2 <- create_fry_spaceranger_spe(fry_unfiltered_dir[2], 
                                                   spaceranger_dir[2], 
                                                   sample_ids[2])
<<<<<<< HEAD
Rows: 4992 Columns: 6
── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): barcode
dbl (5): in_tissue, array_row, array_col, pxl_row_in_fullres, pxl_col_in_fullres

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Joining, by = "barcode"
# read in combined fry and spaceranger spe for fry knee
fry_sketch_spe_1 <- create_fry_spaceranger_spe(fry_sketch_dir[1], 
                                             spaceranger_dir[1], 
                                             sample_ids[1])
Rows: 4992 Columns: 6
── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): barcode
dbl (5): in_tissue, array_row, array_col, pxl_row_in_fullres, pxl_col_in_fullres

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Joining, by = "barcode"
fry_sketch_spe_2 <- create_fry_spaceranger_spe(fry_sketch_dir[2], 
                                             spaceranger_dir[2], 
                                             sample_ids[2])
Rows: 4992 Columns: 6
── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
=======

Rows: 4992 Columns: 6
── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────
>>>>>>> benchmarking-spatial-fry-unfiltered
Delimiter: ","
chr (1): barcode
dbl (5): in_tissue, array_row, array_col, pxl_row_in_fullres, pxl_col_in_fullres

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Joining, by = "barcode"
# spaceranger output paths
spaceranger_dir <- file.path(quants_dir, "cellranger", cellranger_folders)

# read in spaceranger output directly using read10XVisium
spaceranger_spe_1 <- read10xVisium(file.path(spaceranger_dir[1], "outs"), sample_id = sample_ids[1])
spaceranger_spe_2 <- read10xVisium(file.path(spaceranger_dir[2], "outs"), sample_id = sample_ids[2])

Per Spot QC Metrics

Now that we have read in the data and created our two SpatialExperiment objects, we can go ahead and combine them into one list and then calculate the per spot QC metrics using scuttle::addPerCellQCMetrics().

# create one list with both spe's together
all_spe_list <- list(fry_knee_spe_1, fry_unfiltered_spe_1, 
                     fry_sketch_spe_1, spaceranger_spe_1, 
                     fry_knee_spe_2, fry_unfiltered_spe_2, 
                     fry_sketch_spe_2, spaceranger_spe_2)

# name each spe with combination of sample_id-tool 
spe_names <- c("SCPCR000372-alevin-fry-knee", "SCPCR000372-alevin-fry-unfiltered", 
               "SCPCR000372-alevin-fry-unfiltered-sketch", "SCPCR000372-spaceranger",
               "SCPCR000373-alevin-fry-knee", "SCPCR000373-alevin-fry-unfiltered", 
               "SCPCR000373-alevin-fry-unfiltered-sketch", "SCPCR000373-spaceranger")
names(all_spe_list) <- spe_names

# calculate per cell QC and output to a combined data frame with plotting 
all_spe_list <- all_spe_list %>%
    purrr::map(
      ~ scuttle::addPerCellQCMetrics(.x, 
                                     subsets = list(mito = mito_genes[mito_genes %in% rownames(.x)])))

After adding in the per spot QC metrics to both of the spe’s, we want to extract the colData from each spe and create a data frame that we can use for plotting. We will also need some information about each sample and how it was run, so we will create a sample metadata table, sample_info_df that will then be merged with the colData.

# create sample info dataframe to be joined with per spot dataframe later
sample_info_df <- quant_info_table(data_dir= quants_dir, 
                 tools = c("cellranger", "alevin-fry-knee", "alevin-fry-unfiltered"),
                 samples = sample_ids) %>%
  # convert cellranger to spaceranger and paste filtering strategy to alevin-fry
  dplyr::mutate(tool = ifelse(tool == "cellranger", "spaceranger", paste(tool, filter_strategy, sep = "-")),
                tool = dplyr::case_when(alevin_alignment == "sketch" ~ paste(tool, alevin_alignment, sep = "-"),
                                        alevin_alignment != "sketch" ~ tool))

sample_info_df
<<<<<<< HEAD ======= >>>>>>> benchmarking-spatial-fry-unfiltered

When we convert the colData to a data frame we use the custom function, spatial_coldata_to_df() to do so and apply it to each spe in our list.

fry_knee_names <- c("SCPCR000372-alevin-fry-knee", "SCPCR000373-alevin-fry-knee")
fry_unfiltered_names <- c("SCPCR000372-alevin-fry-unfiltered", "SCPCR000373-alevin-fry-unfiltered")
fry_unfiltered_sketch_names <- c("SCPCR000372-alevin-fry-unfiltered-sketch", "SCPCR000373-alevin-fry-unfiltered-sketch")
spaceranger_names <- c("SCPCR000372-spaceranger", "SCPCR000373-spaceranger")

# join coldata dataframe with sample info
coldata_df <- all_spe_list %>%
  purrr::map_df(spatial_coldata_to_df, .id = "tool") %>%
  # remove extra -1 from spaceranger barcodes
  dplyr::mutate(spot_id = gsub("-1", "", spot_id),
                # remove tool from sample id 
                sample_id = stringr::word(sample_id, 1, sep = "-"),
                # remove sample id from tool 
                tool = dplyr::case_when(tool %in% fry_knee_names ~ "alevin-fry-knee",
                                        tool %in% fry_unfiltered_names ~ "alevin-fry-unfiltered",
                                        tool %in% fry_unfiltered_sketch_names ~ "alevin-fry-unfiltered-sketch", 
                                        tool %in% spaceranger_names ~ "spaceranger")) %>%
  dplyr::left_join(sample_info_df,
                   by = c("tool", "sample_id" = "sample")) %>%
  # remove spots that are not overlapping tissue 
  dplyr::filter(in_tissue == 1)

Now we only want to filter our data frame to contain spots that are shared between both tools.

# identify shared spots only 
spot_counts <- coldata_df %>%
  dplyr::count(spot_id, sample_id)
# how many spots are shared among the tools
spot_counts_plot <- coldata_df %>%
  dplyr::group_by(spot_id, sample_id) %>%
  dplyr::summarise(tools_detected = list(unique(tool)))
`summarise()` has grouped output by 'spot_id'. You can override using the `.groups` argument.
ggplot(spot_counts_plot, aes(x = tools_detected))+
  geom_bar() +
  scale_x_upset(n_intersections = 4)

For the most part, the majority of the spots identified are found in both Spaceranger alone and the combination with Alevin-fry-knee and Alevin-fry-unfiltered, with a small subset being identified in Spaceranger and Alevin-fry-unfiltered. It appears that using alevin-fry-unfiltered does give us some spots that using the knee method does not give us and we don’t see any loss of spots.

Let’s filter to only include these common spots.

common_spots <- spot_counts %>%
  dplyr::filter(n == 4) %>%
  dplyr::pull(spot_id)

coldata_df_common <- coldata_df %>%
  dplyr::filter(spot_id %in% common_spots)

We will also need to filter the spe’s directly based on spots that are present in the tissue, so we create a small function to do this and then apply it to both spe’s in the list.

# we will also want to filter the spe's directly 
filter_spe <- function(spe){
  spe <- spe[, spatialData(spe)$in_tissue == 1]
}

all_spe_filter <- all_spe_list %>%
  purrr::map(filter_spe)

When we look at our results, we will also want to visualize them so we will make a custom function to plot the results.

# custom function for plotting spe results and coloring by column of colData of choice
plot_spe <- function(spe, sample, column){
  # plot spots only 
  p1 <- ggspavis::plotSpots(spe, 
                            x_coord = "pxl_col_in_fullres", 
                            y_coord = "pxl_row_in_fullres", 
                            annotate = column) +
    scale_color_viridis_c()
  
  # plot with tissue underneath
  p2 <- ggspavis::plotVisium(spe, 
                             x_coord = "pxl_col_in_fullres", 
                             y_coord = "pxl_row_in_fullres", 
                             fill = column) +
    scale_fill_viridis_c()
  
  # arrange plots and add sample name as title 
  grid.arrange(p1, p2, nrow = 1, top = grid::textGrob(sample))
}

First we will look at the per cell metrics: mitochondrial reads per cell, total UMI per cell, and total genes detected per cell.

# % mitochondrial reads/ spot 
ggplot(coldata_df_common, aes(x = tool, y = subsets_mito_percent, fill = tool)) + 
  geom_boxplot() +
  facet_wrap(~ sample_id) +
  theme_classic() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) + 
  ylab("Mito Percent") + 
  xlab("")

all_spe_filter %>%
  purrr::iwalk(plot_spe, column = "subsets_mito_percent")
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.

Overall it looks like mitochondrial content is low and fairly similar across both tools.

# total UMI/ spot 
ggplot(coldata_df_common, aes(x = sum, color = tool)) + 
  geom_density() + 
  facet_wrap(~ sample_id) +
  theme_classic() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) + 
  ylab("UMI/spot") + 
  xlab("")

all_spe_filter %>%
  purrr::iwalk(plot_spe, column = "sum")
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.

# total genes/ spot 
ggplot(coldata_df_common, aes(x = detected, color = tool)) + 
  geom_density() +
  facet_wrap(~ sample_id) +
  theme_classic() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) + 
  ylab("Genes detected/spot") + 
  xlab("") 

all_spe_filter %>%
  purrr::iwalk(plot_spe, column = "detected")
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.

Generally it looks like the tools are fairly similar, except that Alevin-fry shows a slight decrease in both total UMI/cell and genes detected/cell when compared to Spaceranger alone, which is seen in the spatial plot as well. Alevin-fry-knee and alevin-fry-unfiltered seem to almost completely overlap in terms of quantification with the only difference being a few spots that were not detected using the knee method are now identified in the unfiltered method. If using alevin-fry with --sketch we see that the UMI/cell and genes detected/cell almost completely overlap the counts observed in Spaceranger. This is also seen in the plots, as the plots visually look almost identical when using --sketch but not selective alignment.

Per Gene QC Metrics

Let’s also look at the correlation of mean gene expression across shared genes. We will first need to calculate the per feature QC on the filtered spes after removing spots not present in the tissue and then grab the rowData and combine into a data frame used for plotting.

all_spe_filter <- all_spe_filter %>%
  purrr::map(scuttle::addPerFeatureQCMetrics)
# grab rowdata and combine with sample info
rowdata_df <- purrr::map_df(all_spe_filter, scpcaTools::rowdata_to_df, .id = "tool") %>%
  # extract sample_id from tool and create a new column to avoid duplicates
  dplyr::mutate(sample_id = stringr::word(tool, 1, sep = "-"),
                tool = dplyr::case_when(tool %in% fry_knee_names ~ "alevin-fry-knee",
                                        tool %in% fry_unfiltered_names ~ "alevin-fry-unfiltered",
                                        tool %in% fry_unfiltered_sketch_names ~ "alevin-fry-unfiltered-sketch", 
                                        tool %in% spaceranger_names ~ "spaceranger")) %>%
  dplyr::left_join(sample_info_df,
                   by = c("tool", "sample_id" = "sample"))

We then want to filter out any lowly detected genes, (detected < 5.0) and restrict our analysis to those genes that are found in both tools.

gene_counts <- rowdata_df %>% 
  # remove genes that have a low frequency of being detected
  dplyr::filter(detected >= 5.0) %>%
  dplyr::count(gene_id, sample_id)

# restrict to only common genes 
common_genes <- gene_counts %>%
  dplyr::filter(n == 4) %>%
  dplyr::pull(gene_id)

rowdata_df_common <- rowdata_df %>%
  dplyr::filter(gene_id %in% common_genes) 
# create a table to calculate correlation between mean gene expression
rowdata_cor <- rowdata_df_common %>%
  dplyr::select(tool, gene_id, sample_id, mean) %>%
  # spread the mean expression stats to one column per caller
  tidyr::pivot_wider(id_cols = c(gene_id, sample_id),
                     names_from = c("tool"),
                     values_from = mean) %>%
  # drop rows with NA values to ease correlation calculation
  tidyr::drop_na()
# look at correlation between the two tools
rowdata_cor %>% 
  dplyr::group_by(sample_id) %>%
  dplyr::summarize(
    alevin_fry_knee_spaceranger_cor = cor(`spaceranger`, `alevin-fry-knee`, method = "spearman"),
    alevin_fry_unfiltered_spaceranger_cor= cor(`spaceranger`, `alevin-fry-unfiltered`, method = "spearman"),
    alevin_fry_unfiltered_sketch_spaceranger_cor = cor(`spaceranger`, `alevin-fry-unfiltered-sketch`, method = "spearman")
  )
<<<<<<< HEAD ======= >>>>>>> benchmarking-spatial-fry-unfiltered
# mean gene expression across shared genes 
ggplot(rowdata_cor, aes(x = `spaceranger`, y = `alevin-fry-knee`)) +
  geom_point(size = 0.5, alpha = 0.1) + 
  scale_x_log10() + 
  scale_y_log10() + 
  facet_wrap(~sample_id) +
  geom_abline() +
  labs(x = "Spaceranger mean gene expression", y = "Alevin Fry Knee Mean gene expression") + 
  theme_classic()

NA
ggplot(rowdata_cor, aes(x = `spaceranger`, y = `alevin-fry-unfiltered`)) +
  geom_point(size = 0.5, alpha = 0.1) + 
  scale_x_log10() + 
  scale_y_log10() + 
  facet_wrap(~sample_id) +
  geom_abline() +
  labs(x = "Spaceranger mean gene expression", y = "Alevin Fry Unfiltered Mean gene expression") + 
  theme_classic()

ggplot(rowdata_cor, aes(x = `spaceranger`, y = `alevin-fry-unfiltered-sketch`)) +
  geom_point(size = 0.5, alpha = 0.1) + 
  scale_x_log10() + 
  scale_y_log10() + 
  facet_wrap(~sample_id) +
  geom_abline() +
  labs(x = "Spaceranger mean gene expression", y = "Alevin Fry Unfiltered Sketch Mean gene expression") + 
  theme_classic()

Correlation appears to be quite high between mean gene expression in Spaceranger and Alevin-fry, however, we do see that generally genes have higher gene expression in Spaceranger than in Alevin-fry and are slightly off the diagonal.

In alevin-fry --sketch the genes line up along the x=y axis and appear to have less of an increase in expression with Spaceranger quantification, however the group of genes that are slightly off the diagonal is still present.

There is a subset of genes that seems to be slightly more affected and be further off the diagonal. Let’s take at those gene for each sample. It does appear to be less dramatic in SCPCR000373. We will only focus on without --sketch, as that is where we see the largest inmpact on gene expression differences.

# get the gene symbols and then join back with rowdata df 
gene_symbols_df <- rowdata_df %>%
  dplyr::select(gene_id, symbol)

# join correlation with gene symbols 
rowdata_cor <- rowdata_cor %>%
  dplyr::left_join(gene_symbols_df) %>%
  dplyr::distinct() %>%
  tidyr::drop_na() %>%
  # add difference in mean gene expression between alevin-fry and spaceranger 
  dplyr::mutate(knee_log_fold_change = log(`alevin-fry-knee`/spaceranger),
                unfiltered_log_fold_change = log(`alevin-fry-unfiltered`/`spaceranger`))
Joining, by = "gene_id"

Let’s see if we can specificall identify the group of genes that are off the diagonal by labeling them with a different color.

rowdata_cor <- rowdata_cor %>%
  dplyr::mutate(knee_diff = ifelse(
    knee_log_fold_change < - 0.75 & 
      spaceranger < 5 & 
      spaceranger > -1, "diff_expression", "equal_expression"),
    unfiltered_diff = ifelse(
    unfiltered_log_fold_change < - 0.75 & 
      spaceranger < 5 & 
      spaceranger > -1, "diff_expression", "equal_expression"))
ggplot(rowdata_cor, aes(x = `spaceranger`, y = `alevin-fry-knee`, color = knee_diff)) +
  geom_point(size = 0.5, alpha = 0.1) + 
  scale_x_log10() + 
  scale_y_log10() + 
  geom_abline() +
  facet_wrap(~ sample_id, nrow = 2) + 
  labs(x = "Spaceranger mean gene expression", y = "Alevin Fry Knee Mean gene expression") + 
  theme_classic()

ggplot(rowdata_cor, aes(x = `spaceranger`, y = `alevin-fry-unfiltered`, color = unfiltered_diff)) +
  geom_point(size = 0.5, alpha = 0.1) + 
  scale_x_log10() + 
  scale_y_log10() + 
  geom_abline() +
  facet_wrap(~ sample_id, nrow = 2) + 
  labs(x = "Spaceranger mean gene expression", y = "Alevin Fry Unfiltered Mean gene expression") + 
  theme_classic()

It looks like the genes that this separate group of genes have a log(fold change) < -0.75 and mean gene expression in spaceranger < 5 and > -1. Let’s get that list of genes in both alevin-fry-knee and alevin-fry-unfiltered.

knee_diff_gene_counts <- rowdata_cor %>%
  dplyr::filter(knee_diff == "diff_expression") %>%
  dplyr::count(gene_id) %>% 
  dplyr::filter(n == 2) %>%
  dplyr::pull(gene_id)

knee_diff_genes <- rowdata_cor %>%
  dplyr::filter(gene_id %in% knee_diff_gene_counts) %>%
  dplyr::arrange(symbol)

knee_diff_genes
unfiltered_diff_gene_counts <- rowdata_cor %>%
  dplyr::filter(unfiltered_diff == "diff_expression") %>%
  dplyr::count(gene_id) %>% 
  dplyr::filter(n == 2) %>%
  dplyr::pull(gene_id)

unfiltered_diff_genes <- rowdata_cor %>%
  dplyr::filter(gene_id %in% unfiltered_diff_gene_counts) %>%
  dplyr::arrange(symbol)

unfiltered_diff_genes

Let’s look at what types of genes are found to have different gene expression across these two tools using over representation analysis. First, let’s look specifically at that group of genes that is slightly off the diagonal (considering only genes found in both samples), then we will look at all genes with abs(log(fold change) > 0.5)

# unfiltered and knee outlier gene lists
knee_diff_genes <- knee_diff_genes$symbol %>%
  unique()

unfiltered_diff_genes <- unfiltered_diff_genes$symbol %>%
  unique()

# background gene list 
background_genes <- rowdata_cor$symbol %>%
  unique()
knee_go_ora_results <- enrichGO(gene = knee_diff_genes,
                                universe = background_genes,
                                keyType = "SYMBOL",
                                OrgDb = org.Hs.eg.db,
                                ont = "BP",
                                pAdjustMethod = "BH",
                                pvalueCutoff = 0.00001)

unfiltered_go_ora_results <- enrichGO(gene = unfiltered_diff_genes,
                                      universe = background_genes,
                                      keyType = "SYMBOL",
                                      OrgDb = org.Hs.eg.db,
                                      ont = "BP",
                                      pAdjustMethod = "BH",
                                      pvalueCutoff = 0.00001)
knee_go_results <- knee_go_ora_results@result %>%
  as.data.frame() %>%
  dplyr::filter(p.adjust < 0.2)
knee_go_results
unfiltered_go_results <- unfiltered_go_ora_results@result %>%
  as.data.frame() %>%
  dplyr::filter(p.adjust < 0.2)
unfiltered_go_results

In order to see any enrichment results, the adjusted p-value threshold has to be increased to 0.2 so there is very little confidence that these pathways are actually enriched. Therefore, it appears that there are no significant pathways that are affected by the group of genes that are found to be off the diagonal between spaceranger and alevin-fry.

Let’s take a look at all genes that have at least 0.5 log fold change between Spaceranger and Alevin-fry.

# find genes that have different gene expression in both samples between spaceranger and fry-knee or fry-unfiltered 
different_gene_counts <- rowdata_cor %>%
  dplyr::filter(abs(knee_log_fold_change) > 0.5 | abs(unfiltered_log_fold_change) > 0.5) %>%
  dplyr::count(gene_id) %>% 
  dplyr::filter(n == 2) %>%
  dplyr::pull(gene_id)
# print out list of genes with with > 1.5 gene expression in Alevin-fry 
# only include genes that are found in both samples 
rowdata_cor %>%
  dplyr::filter(gene_id %in% different_gene_counts) %>%
  dplyr::arrange(symbol) %>%
  dplyr::select(sample_id, symbol, gene_id, knee_log_fold_change, 
                unfiltered_log_fold_change, `alevin-fry-knee`, 
                `alevin-fry-unfiltered`, spaceranger)
# extract target gene list for ORA, 886 genes
different_genes <- rowdata_cor %>%
  # filter for anything with fold change > 1.5 and found in both samples
  dplyr::filter(gene_id %in% different_gene_counts) %>%
  dplyr::pull(symbol) %>%
  unique()

We are only going to use one list of genes here to do over representation analysis, because the same genes are found to have different gene expression between Alevin-fry and spaceranger regardless of filtering strategy for Alevin-fry.

# perform gene ontology looking at all genes that are different 
go_ora_results <- enrichGO(gene = different_genes,
                           universe = background_genes,
                           keyType = "SYMBOL",
                           OrgDb = org.Hs.eg.db,
                           ont = "BP",
                           pAdjustMethod = "BH",
                           pvalueCutoff = 0.00001)
# look at gene ontology results 
go_results <- go_ora_results@result %>%
  as.data.frame() %>%
  dplyr::filter(p.adjust < 0.2)
go_results

It looks like when you filter to only include genes that are found to have different mean gene expression between Spaceranger and Alevin-fry in both samples there are no specific pathways identified that the genes belong to.

Shared genes across tools

The final thing we will look at is the overlap of genes detected in each tool. First we need to filter the SpatialExperiment objects to only have spots that are found in both tools and then we filter to include only genes that are found in both indices before looking at the overlap.

# make a function to filter sces by subset of spots and re-calculate feature stats
filter_spe <- function(spe, spots){
  # remove "-1" at end of barcode for spaceranger spes
  colnames(spe) <- gsub("-1", "", colnames(spe))
  cells_to_keep <- colnames(spe) %in% spots
  rowData(spe) <- NULL
  spe[, cells_to_keep] %>%
    scuttle::addPerFeatureQCMetrics()
}
<<<<<<< HEAD ======= >>>>>>> benchmarking-spatial-fry-unfiltered
# filter all spes to only have common spots 
spe_list_common <- all_spe_filter %>%
  purrr::map(filter_spe, spots = common_spots)

# grab rowdata from filtered sces 
rowdata_df_filtered <- purrr::map_df(spe_list_common, scpcaTools::rowdata_to_df, .id = "tool") %>%
  dplyr::mutate(sample_id = stringr::word(tool, 1, sep = "-"),
                tool = dplyr::case_when(tool %in% fry_knee_names ~ "alevin-fry-knee",
                                        tool %in% fry_unfiltered_names ~ "alevin-fry-unfiltered",
                                        tool %in% fry_unfiltered_sketch_names ~ "alevin-fry-unfiltered-sketch", 
                                        tool %in% spaceranger_names ~ "spaceranger")) %>%
  dplyr::left_join(sample_info_df,
                   by = c("tool", "sample_id" = "sample"))
# get genes common in all tools
common_genes <- rowdata_df_filtered %>%
  dplyr::select(gene_id, tool) %>%
  dplyr::distinct() %>%
  dplyr::group_by(gene_id) %>%
  dplyr::tally() %>%
  # filter for genes found in both spaceranger and alevin-fry
  dplyr::filter(n == 4) %>%
  dplyr::pull(gene_id)

# filter rowdata_df to only include genes found in all tools and genes with mean > 0 and detected > 0 in all cells
rowdata_df_filtered <- rowdata_df_filtered %>% 
  dplyr::filter(gene_id %in% common_genes) %>%
  dplyr::filter(mean > 0 & detected > 0)
# create upset plot 
gene_detect_df <- rowdata_df_filtered %>%
  dplyr::group_by(gene_id) %>%
  dplyr::summarise(tools_detected = list(unique(tool)))

ggplot(gene_detect_df, aes(x = tools_detected)) +
  geom_bar() +
  scale_x_upset(n_intersections = 5)

<<<<<<< HEAD

For the most part genes are found in all tools, however it looks like there is a chunk of genes that we would miss that are found in Spaceranger that are not found in either of the Alevin-fry tools. It also appears that there is a group of genes specific to Alevin-fry-unfiltered --sketch and a group of genes that are only found to overlap between Spaceranger and Alevin-fry with --sketch. Are any of the genes that are specific to Spaceranger genes involved in important pathways that we might want to make sure that we don’t lose?

=======

For the most part genes are found in all tools, however it looks like there is a chunk of genes that we would miss that are found in Spaceranger that are not found in either of the Alevin-fry tools. Are any of those genes involved in important pathways that we might want to make sure that we don’t lose? First, we can see what types of genes there are by grabbing the gene annotation from the gtf file.

>>>>>>> benchmarking-spatial-fry-unfiltered
# load gtf file and select gene name and gene_biotype
gtf_file <- file.path(base_dir, "sample-info", "Homo_sapiens.GRCh38.104.gtf.gz")
gtf <- rtracklayer::import(gtf_file, feature.type = "gene") %>%
  as.data.frame() %>%
  dplyr::select(gene_id, gene_name, gene_biotype)
# join spaceranger only genes with gene biotypes from gtf
spaceranger_only_gene_table <- gene_detect_df %>%
  dplyr::filter(tools_detected == "spaceranger") %>%
  dplyr::select(gene_id) %>%
  dplyr::left_join(gtf, by= "gene_id") 

# create a table of gene biotypes that are specific to spaceranger
spaceranger_gene_counts <- spaceranger_only_gene_table %>%
  dplyr::count(gene_biotype) %>% 
  # reorder by gene biotype
  dplyr::mutate(gene_biotype =factor(gene_biotype, levels = gene_biotype[order(n)]))
ggplot(spaceranger_gene_counts, aes(y = gene_biotype, x = n)) + 
  geom_bar(stat = "identity") + 
  xlab("")

It looks like the majority of the genes that we lose from not using Spaceranger correspond to pseudogenes and long-noncoding RNA’s, with protein coding genes being the third most represented category. We can also do over-representation analysis and see if any genes in specific pathways are lost.

# spaceranger only genes to do ORA 
spaceranger_only_genes <- spaceranger_only_gene_table %>%
  unique() %>%
  dplyr::pull(gene_name)
spaceranger_ora_results <- enrichGO(gene = spaceranger_only_genes,
                           universe = background_genes,
                           keyType = "SYMBOL",
                           OrgDb = org.Hs.eg.db,
                           ont = "BP",
                           pAdjustMethod = "BH",
                           pvalueCutoff = 0.00001)
# look at gene ontology results 
spaceranger_go_results <- spaceranger_ora_results@result %>%
  as.data.frame() %>%
  dplyr::filter(p.adjust < 0.2)
spaceranger_go_results
# save spe list
spe_file <- file.path(results_dir, "all_spe_list_ensembl_v104.rds")
readr::write_rds(all_spe_list, spe_file)

Concluding thoughts

  • Alevin-fry and Spaceranger only result in similar distributions of UMI/spot and genes/spot, although it is not quite as nice as the overlay you see with single-cell libraries.
  • Using alevin-fry-unfiltered results in identification of the same spots that are identified in Spaceranger.
  • It appears that Spaceranger has slightly higher UMI/cell and genes detected/cell.
  • The increase in UMI/cell and genes detected/cell in Spaceranger disappears with use of --sketch alignment with Alevin-fry.
  • Both tools also show high correlation in mean gene expression and high overlap in the genes that are detected, although there is an increase in gene expression in Spaceranger alone.
  • The increase in gene expression found in Spaceranger also decreases through use of --sketch alignment and more genes that were quantified in Spaceranger and were not quantified in Alevin-fry with selective alignment are now quantified with --sketch.

Session Info

sessioninfo::session_info()
─ Session info ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 setting  value
 version  R version 4.1.1 (2021-08-10)
 os       macOS Catalina 10.15.7
 system   x86_64, darwin17.0
 ui       RStudio
 language (EN)
 collate  en_US.UTF-8
 ctype    en_US.UTF-8
 tz       America/Chicago
 date     2021-12-15
 rstudio  1.4.1106 Tiger Daylily (desktop)
 pandoc   2.11.4 @ /Applications/RStudio.app/Contents/MacOS/pandoc/ (via rmarkdown)

─ Packages ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 package              * version  date (UTC) lib source
 AnnotationDbi        * 1.56.2   2021-11-09 [1] Bioconductor
 ape                    5.5      2021-04-25 [1] CRAN (R 4.1.0)
 aplot                  0.1.1    2021-09-22 [1] CRAN (R 4.1.0)
 assertthat             0.2.1    2019-03-21 [1] CRAN (R 4.1.0)
 beachmat               2.10.0   2021-10-26 [1] Bioconductor
 Biobase              * 2.54.0   2021-10-26 [1] Bioconductor
 BiocGenerics         * 0.40.0   2021-10-26 [1] Bioconductor
 BiocIO                 1.4.0    2021-10-26 [1] Bioconductor
 BiocParallel           1.28.2   2021-11-25 [1] Bioconductor
 Biostrings             2.62.0   2021-10-26 [1] Bioconductor
 bit                    4.0.4    2020-08-04 [1] CRAN (R 4.1.0)
 bit64                  4.0.5    2020-08-30 [1] CRAN (R 4.1.0)
 bitops                 1.0-7    2021-04-24 [1] CRAN (R 4.1.0)
 blob                   1.2.2    2021-07-23 [1] CRAN (R 4.1.0)
 bslib                  0.3.1    2021-10-06 [1] CRAN (R 4.1.0)
 cachem                 1.0.6    2021-08-19 [1] CRAN (R 4.1.0)
 cli                    3.1.0    2021-10-27 [1] CRAN (R 4.1.1)
 clusterProfiler      * 4.2.0    2021-10-26 [1] Bioconductor
 colorspace             2.0-2    2021-06-24 [1] CRAN (R 4.1.0)
 crayon                 1.4.2    2021-10-29 [1] CRAN (R 4.1.0)
 data.table             1.14.2   2021-09-27 [1] CRAN (R 4.1.0)
 DBI                    1.1.1    2021-01-15 [1] CRAN (R 4.1.0)
 DelayedArray           0.20.0   2021-10-26 [1] Bioconductor
 DelayedMatrixStats     1.16.0   2021-10-26 [1] Bioconductor
 digest                 0.6.29   2021-12-01 [1] CRAN (R 4.1.0)
 DO.db                  2.9      2021-11-30 [1] Bioconductor
 DOSE                   3.20.1   2021-11-18 [1] Bioconductor
 downloader             0.4      2015-07-09 [1] CRAN (R 4.1.0)
 dplyr                  1.0.7    2021-06-18 [1] CRAN (R 4.1.0)
 dqrng                  0.3.0    2021-05-01 [1] CRAN (R 4.1.0)
 DropletUtils           1.14.1   2021-11-08 [1] Bioconductor
 edgeR                  3.36.0   2021-10-26 [1] Bioconductor
 ellipsis               0.3.2    2021-04-29 [1] CRAN (R 4.1.0)
 enrichplot             1.14.1   2021-10-31 [1] Bioconductor
 evaluate               0.14     2019-05-28 [1] CRAN (R 4.1.0)
 fansi                  0.5.0    2021-05-25 [1] CRAN (R 4.1.0)
 farver                 2.1.0    2021-02-28 [1] CRAN (R 4.1.0)
 fastmap                1.1.0    2021-01-25 [1] CRAN (R 4.1.0)
 fastmatch              1.1-3    2021-07-23 [1] CRAN (R 4.1.0)
 fgsea                  1.20.0   2021-10-26 [1] Bioconductor
 generics               0.1.1    2021-10-25 [1] CRAN (R 4.1.0)
 GenomeInfoDb         * 1.30.0   2021-10-26 [1] Bioconductor
 GenomeInfoDbData       1.2.7    2021-11-16 [1] Bioconductor
 GenomicAlignments      1.30.0   2021-10-26 [1] Bioconductor
 GenomicRanges        * 1.46.1   2021-11-18 [1] Bioconductor
 ggforce                0.3.3    2021-03-05 [1] CRAN (R 4.1.0)
 ggfun                  0.0.4    2021-09-17 [1] CRAN (R 4.1.0)
 ggplot2              * 3.3.5    2021-06-25 [1] CRAN (R 4.1.0)
 ggplotify              0.1.0    2021-09-02 [1] CRAN (R 4.1.0)
 ggraph                 2.0.5    2021-02-23 [1] CRAN (R 4.1.0)
 ggrepel              * 0.9.1    2021-01-15 [1] CRAN (R 4.1.0)
 ggside                 0.1.3    2021-10-24 [1] CRAN (R 4.1.0)
 ggspavis               1.0.0    2021-10-26 [1] Bioconductor
 ggtree                 3.2.1    2021-11-16 [1] Bioconductor
 ggupset              * 0.3.0    2020-05-05 [1] CRAN (R 4.1.0)
 glue                   1.5.1    2021-11-30 [1] CRAN (R 4.1.1)
 GO.db                  3.14.0   2021-11-30 [1] Bioconductor
 GOSemSim               2.20.0   2021-10-26 [1] Bioconductor
 graphlayouts           0.7.2    2021-11-21 [1] CRAN (R 4.1.0)
 gridExtra            * 2.3      2017-09-09 [1] CRAN (R 4.1.0)
 gridGraphics           0.5-1    2020-12-13 [1] CRAN (R 4.1.0)
 grr                    0.9.5    2016-08-26 [1] CRAN (R 4.1.0)
 gtable                 0.3.0    2019-03-25 [1] CRAN (R 4.1.0)
 HDF5Array              1.22.1   2021-11-14 [1] Bioconductor
 here                   1.0.1    2020-12-13 [1] CRAN (R 4.1.0)
 hms                    1.1.1    2021-09-26 [1] CRAN (R 4.1.0)
 htmltools              0.5.2    2021-08-25 [1] CRAN (R 4.1.0)
 httr                   1.4.2    2020-07-20 [1] CRAN (R 4.1.0)
 igraph                 1.2.9    2021-11-23 [1] CRAN (R 4.1.0)
 IRanges              * 2.28.0   2021-10-26 [1] Bioconductor
 jquerylib              0.1.4    2021-04-26 [1] CRAN (R 4.1.0)
 jsonlite               1.7.2    2020-12-09 [1] CRAN (R 4.1.0)
 KEGGREST               1.34.0   2021-10-26 [1] Bioconductor
 knitr                  1.36     2021-09-29 [1] CRAN (R 4.1.0)
 labeling               0.4.2    2020-10-20 [1] CRAN (R 4.1.0)
 lattice                0.20-45  2021-09-22 [1] CRAN (R 4.1.0)
 lazyeval               0.2.2    2019-03-15 [1] CRAN (R 4.1.0)
 lifecycle              1.0.1    2021-09-24 [1] CRAN (R 4.1.0)
 limma                  3.50.0   2021-10-26 [1] Bioconductor
 locfit                 1.5-9.4  2020-03-25 [1] CRAN (R 4.1.0)
 magick                 2.7.3    2021-08-18 [1] CRAN (R 4.1.0)
 magrittr             * 2.0.1    2020-11-17 [1] CRAN (R 4.1.0)
 MASS                   7.3-54   2021-05-03 [1] CRAN (R 4.1.1)
 Matrix                 1.3-4    2021-06-01 [1] CRAN (R 4.1.1)
 Matrix.utils           0.9.8    2020-02-26 [1] CRAN (R 4.1.0)
 MatrixGenerics       * 1.6.0    2021-10-26 [1] Bioconductor
 matrixStats          * 0.61.0   2021-09-17 [1] CRAN (R 4.1.0)
 memoise                2.0.1    2021-11-26 [1] CRAN (R 4.1.0)
 munsell                0.5.0    2018-06-12 [1] CRAN (R 4.1.0)
 nlme                   3.1-153  2021-09-07 [1] CRAN (R 4.1.0)
 org.Hs.eg.db         * 3.14.0   2021-11-16 [1] Bioconductor
 patchwork              1.1.1    2020-12-17 [1] CRAN (R 4.1.0)
 pillar                 1.6.4    2021-10-18 [1] CRAN (R 4.1.1)
 pkgconfig              2.0.3    2019-09-22 [1] CRAN (R 4.1.0)
 plyr                   1.8.6    2020-03-03 [1] CRAN (R 4.1.0)
 png                    0.1-7    2013-12-03 [1] CRAN (R 4.1.0)
 polyclip               1.10-0   2019-03-14 [1] CRAN (R 4.1.0)
 purrr                  0.3.4    2020-04-17 [1] CRAN (R 4.1.0)
 qvalue                 2.26.0   2021-10-26 [1] Bioconductor
 R.methodsS3            1.8.1    2020-08-26 [1] CRAN (R 4.1.0)
 R.oo                   1.24.0   2020-08-26 [1] CRAN (R 4.1.0)
 R.utils                2.11.0   2021-09-26 [1] CRAN (R 4.1.0)
 R6                     2.5.1    2021-08-19 [1] CRAN (R 4.1.0)
 RColorBrewer           1.1-2    2014-12-07 [1] CRAN (R 4.1.0)
 Rcpp                   1.0.7    2021-07-07 [1] CRAN (R 4.1.0)
 RCurl                  1.98-1.5 2021-09-17 [1] CRAN (R 4.1.0)
 readr                  2.1.1    2021-11-30 [1] CRAN (R 4.1.1)
 reshape2               1.4.4    2020-04-09 [1] CRAN (R 4.1.0)
 restfulr               0.0.13   2017-08-06 [1] CRAN (R 4.1.0)
 rhdf5                  2.38.0   2021-10-26 [1] Bioconductor
 rhdf5filters           1.6.0    2021-10-26 [1] Bioconductor
 Rhdf5lib               1.16.0   2021-10-26 [1] Bioconductor
 rjson                  0.2.20   2018-06-08 [1] CRAN (R 4.1.0)
 rlang                  0.4.12   2021-10-18 [1] CRAN (R 4.1.1)
 rmarkdown              2.11     2021-09-14 [1] CRAN (R 4.1.0)
 rprojroot              2.0.2    2020-11-15 [1] CRAN (R 4.1.0)
 Rsamtools              2.10.0   2021-10-26 [1] Bioconductor
 RSQLite                2.2.9    2021-12-06 [1] CRAN (R 4.1.1)
 rstudioapi             0.13     2020-11-12 [1] CRAN (R 4.1.0)
 rtracklayer            1.54.0   2021-10-26 [1] Bioconductor
 S4Vectors            * 0.32.3   2021-11-21 [1] Bioconductor
 sass                   0.4.0    2021-05-12 [1] CRAN (R 4.1.0)
 scales                 1.1.1    2020-05-11 [1] CRAN (R 4.1.0)
 scatterpie             0.1.7    2021-08-20 [1] CRAN (R 4.1.0)
 scpcaTools             0.1.2    2021-10-12 [1] Github (AlexsLemonade/scpcaTools@2cdad4c)
 scuttle                1.4.0    2021-10-26 [1] Bioconductor
 sessioninfo            1.2.2    2021-12-06 [1] CRAN (R 4.1.1)
 shadowtext             0.0.9    2021-09-19 [1] CRAN (R 4.1.0)
 SingleCellExperiment * 1.16.0   2021-10-26 [1] Bioconductor
 sparseMatrixStats      1.6.0    2021-10-26 [1] Bioconductor
 SpatialExperiment    * 1.3.6    2021-12-06 [1] Github (drighelli/SpatialExperiment@ddb15e0)
 stringi                1.7.6    2021-11-29 [1] CRAN (R 4.1.1)
 stringr                1.4.0    2019-02-10 [1] CRAN (R 4.1.0)
 SummarizedExperiment * 1.24.0   2021-10-26 [1] Bioconductor
 tibble                 3.1.6    2021-11-07 [1] CRAN (R 4.1.0)
 tidygraph              1.2.0    2020-05-12 [1] CRAN (R 4.1.0)
 tidyr                  1.1.4    2021-09-27 [1] CRAN (R 4.1.0)
 tidyselect             1.1.1    2021-04-30 [1] CRAN (R 4.1.0)
 tidytree               0.3.6    2021-11-12 [1] CRAN (R 4.1.0)
 tinytex                0.35     2021-11-04 [1] CRAN (R 4.1.0)
 treeio                 1.18.1   2021-11-14 [1] Bioconductor
 tweenr                 1.0.2    2021-03-23 [1] CRAN (R 4.1.0)
 tzdb                   0.2.0    2021-10-27 [1] CRAN (R 4.1.1)
 utf8                   1.2.2    2021-07-24 [1] CRAN (R 4.1.0)
 vctrs                  0.3.8    2021-04-29 [1] CRAN (R 4.1.0)
 viridis                0.6.2    2021-10-13 [1] CRAN (R 4.1.0)
 viridisLite            0.4.0    2021-04-13 [1] CRAN (R 4.1.0)
 vroom                  1.5.7    2021-11-30 [1] CRAN (R 4.1.1)
 withr                  2.4.3    2021-11-30 [1] CRAN (R 4.1.1)
 xfun                   0.28     2021-11-04 [1] CRAN (R 4.1.0)
 XML                    3.99-0.8 2021-09-17 [1] CRAN (R 4.1.0)
 XVector                0.34.0   2021-10-26 [1] Bioconductor
 yaml                   2.2.1    2020-02-01 [1] CRAN (R 4.1.0)
 yulab.utils            0.0.4    2021-10-09 [1] CRAN (R 4.1.0)
 zlibbioc               1.40.0   2021-10-26 [1] Bioconductor

 [1] /Library/Frameworks/R.framework/Versions/4.1/Resources/library

─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
<<<<<<< HEAD
LS0tCnRpdGxlOiAiU3BhdGlhbCBUcmFuc2NyaXB0b21pY3MgQmVuY2htYXJraW5nIgphdXRob3I6ICJBbGx5IEhhd2tpbnMgZm9yIENDREwiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKSW4gdGhpcyBub3RlYm9vayB3ZSBhcmUgY29tcGFyaW5nIHRoZSB1c2Ugb2YgQWxldmluLWZyeSBhbmQgU3BhY2VyYW5nZXIgZm9yIHF1YW50aWZ5aW5nIHNwYXRpYWwgdHJhbnNjcmlwdG9taWNzIGxpYnJhcmllcy4gClR3byBzcGF0aWFsIHRyYW5zY3JpcHRvbWljIGxpYnJhcmllcyB3ZXJlIHF1YW50aWZpZWQgdXNpbmcgQWxldmluLWZyeSBhbmQgU3BhY2VyYW5nZXIgYW5kIHRoZSByZXN1bHRzIHdlcmUgY29tYmluZWQgZm9sbG93aW5nIHRoZSBbQWxldmluLWZyeSB0dXRvcmlhbF0oaHR0cHM6Ly9jb21iaW5lLWxhYi5naXRodWIuaW8vYWxldmluLWZyeS10dXRvcmlhbHMvMjAyMS9hZi1zcGF0aWFsLykuIApXZSBhcmUgY29tcGFyaW5nIHRoYXQgdG8gaWYgd2Ugd2VyZSB0byBub3QgaW50ZWdyYXRlIHRoZSBTcGFjZXJhbmdlciBkYXRhIHdpdGggQWxldmluLWZyeSBhbmQgb25seSB1c2UgU3BhY2VyYW5nZXIuIApXaGVuIHBlcmZvcm1pbmcgdGhpcyBhbmFseXNpcyBhbGwgdG9vbHMgdXNlZCBhbiBpbmRleCB3aXRoIEVuc2VtYmwgMTA0LgpIZXJlIHdlIHdpbGwgbG9vayBhdCB0d28gbGlicmFyaWVzLCBTQ1BDUjAwMDM3MiBhbmQgU0NQQ1IwMDAzNzMuCkFsZXZpbi1mcnkgd2FzIHJ1biBib3RoIHVzaW5nIHRoZSBrbmVlIGZpbHRlcmluZyBhbmQgdGhlIHVuZmlsdGVyZWQgcGVybWl0IGxpc3QgbW9kZS4gCkluY2x1ZGVkIGhlcmUgaXMgYWxzbyB1c2Ugb2YgQWxldmluLWZyeSB3aXRoIHRoZSBgLS1za2V0Y2hgIGFsaWdubWVudCBtb2RlIGFuZCB1bmZpbHRlcmVkIHBlcm1pdCBsaXN0LgoKTm90ZSB0aGF0IGBTcGF0aWFsRXhwZXJpbWVudGAgd2FzIGluc3RhbGxlZCBmcm9tIEdpdGh1YiwgaW4gb3JkZXIgdG8gcmVmbGVjdCB0aGUgbW9zdCByZWNlbnQgY2hhbmdlcyBpbiBgcmVhZDEwWFZpc2l1bWAgYXQgY29tbWl0IGRkYjE1ZTAuCgojIyBTZXQgVXAKCmBgYHtyfQpsaWJyYXJ5KG1hZ3JpdHRyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpCmxpYnJhcnkoU3BhdGlhbEV4cGVyaW1lbnQpCmxpYnJhcnkoZ2d1cHNldCkKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCmxpYnJhcnkob3JnLkhzLmVnLmRiKQoKIyBzZXQgc2VlZCBmb3IgT1JBCnNldC5zZWVkKDIwMjEpCmBgYAoKYGBge3J9CiMgbG9hZCBpbiBiZW5jaG1hcmtpbmcgZnVuY3Rpb25zIHRoYXQgd2lsbCBiZSB1c2VkIGZvciBjb3B5aW5nIGRhdGEgYW5kIGdlbmVyYXRpbmcgc2FtcGxlIHRhYmxlcwpmdW5jdGlvbl9wYXRoIDwtIGZpbGUucGF0aCgiLi4iICwiYmVuY2htYXJraW5nLWZ1bmN0aW9ucyIsICJSIikKZmlsZS5wYXRoKGZ1bmN0aW9uX3BhdGgsIGxpc3QuZmlsZXMoZnVuY3Rpb25fcGF0aCwgcGF0dGVybiA9ICIqLlIkIikpICU+JQogIHB1cnJyOjp3YWxrKHNvdXJjZSkKYGBgCgoKYGBge3J9CiMgc2V0IHVwIGZpbGUgcGF0aHMgCmJhc2VfZGlyIDwtIGhlcmU6OmhlcmUoKQoKIyBmb2xkZXIgd2l0aCBhbGV2aW4tZnJ5IGFuZCBjZWxscmFuZ2VyIHF1YW50cyBmcm9tIFMzCmRhdGFfZGlyIDwtIGZpbGUucGF0aChiYXNlX2RpciwgImRhdGEiLCAic3BhdGlhbCIpIApxdWFudHNfZGlyIDwtIGZpbGUucGF0aChkYXRhX2RpciwgImRhdGEiLCAicXVhbnRzIikKCiMgcmVzdWx0cyBkaXJlY3RvcnkgCnJlc3VsdHNfZGlyIDwtIGZpbGUucGF0aChkYXRhX2RpciwgInJlc3VsdHMiKQoKIyBzYW1wbGUgbmFtZQpzYW1wbGVfaWRzIDwtIGMoIlNDUENSMDAwMzcyIiwgIlNDUENSMDAwMzczIikKYGBgCgoKYGBge3J9Cm1pdG9fZmlsZSA8LSBmaWxlLnBhdGgoYmFzZV9kaXIsICJzYW1wbGUtaW5mbyIsICJIb21vX3NhcGllbnMuR1JDaDM4LjEwMy5taXRvZ2VuZXMudHh0IikKICAKIyByZWFkIGluIG1pdG8gZ2VuZXMgCm1pdG9fZ2VuZXMgPC0gcmVhZHI6OnJlYWRfdHN2KG1pdG9fZmlsZSwgY29sX25hbWVzID0gImdlbmVfaWQiKQptaXRvX2dlbmVzIDwtIG1pdG9fZ2VuZXMgJT4lCiAgZHBseXI6OnB1bGwoZ2VuZV9pZCkgJT4lCiAgdW5pcXVlKCkKYGBgCgojIyBDb3B5IGZyb20gUzMKCmBgYHtyIGV2YWw9RkFMU0V9CiMgZG93bmxvYWQgYWxldmluIGZyeSBhbmQgY2VsbHJhbmdlciBvdXRwdXQKYXdzX2NvcHlfc2FtcGxlcyhsb2NhbF9kaXIgPSBxdWFudHNfZGlyLAogICAgICAgICAgICAgICAgIHMzX2RpciA9ICJzMzovL25leHRmbG93LWNjZGwtcmVzdWx0cy9zY3BjYSIsCiAgICAgICAgICAgICAgICAgc2FtcGxlcyA9IHNhbXBsZV9pZHMsCiAgICAgICAgICAgICAgICAgdG9vbHMgPSBjKCJhbGV2aW4tZnJ5LWtuZWUiLCAiYWxldmluLWZyeS11bmZpbHRlcmVkIiwgImNlbGxyYW5nZXIiKSkKYGBgCgoKIyMgQWxldmluLWZyeSArIFNwYWNlcmFuZ2VyIHZlcnN1cyBTcGFjZXJhbmdlciBPbmx5CgpOb3cgbGV0J3MgdGFrZSBhIGxvb2sgYXQgY29tcGFyaW5nIHRoZSB0d28gbWV0aG9kcyBvZiB1c2luZyBBbGV2aW4tZnJ5ICsgU3BhY2VyYW5nZXIgdG8gb25seSBTcGFjZXJhbmdlciBmb3IgcXVhbnRpZmljYXRpb24uIApUbyBkbyB0aGlzLCB3ZSB3aWxsIHJlYWQgaW4gdGhlIEFsZXZpbi1mcnkgKyBTcGFjZXJhbmdlciBjb21iaW5lZCBhbmQgU3BhY2VyYW5nZXIgb25seSBgU3BhdGlhbEV4cGVyaW1lbnRgIG9iamVjdHMgc2VwYXJhdGVseSBhbmQgdGhlbiBtZXJnZSB0aGVtIGludG8gb25lIGxpc3QgYmVmb3JlIGdyYWJiaW5nIHRoZSBwZXIgY2VsbCBhbmQgcGVyIGdlbmUgcXVhbGl0eSBtZXRyaWNzLiAKCiMjIyBDcmVhdGUgU3BhdGlhbCBFeHBlcmltZW50cyAKCmBgYHtyfQojIGdldCBwYXRoIHRvIGZyeSBrbmVlIG91dHB1dCBkaXJlY3RvcnkgCmZyeV9rbmVlX2RpciA8LSBmaWxlLnBhdGgocXVhbnRzX2RpciwgImFsZXZpbi1mcnkta25lZSIsIHNhbXBsZV9pZHMpCmZyeV9rbmVlX2RpciA8LSBwYXN0ZTAoZnJ5X2tuZWVfZGlyLCAiLUhvbW9fc2FwaWVucy5HUkNoMzguMTA0LnNwbGljZWRfaW50cm9uLnR4b21lLXNhbGlnbi1jci1saWtlLWVtLWtuZWUiKQoKIyBnZXQgcGF0aCB0byBmcnkgdW5maWx0ZXJlZCBvdXRwdXQgZGlyZWN0b3J5IApmcnlfdW5maWx0ZXJlZF9kaXIgPC0gZmlsZS5wYXRoKHF1YW50c19kaXIsICJhbGV2aW4tZnJ5LXVuZmlsdGVyZWQiLCBzYW1wbGVfaWRzKQpmcnlfdW5maWx0ZXJlZF9kaXIgPC0gcGFzdGUwKGZyeV91bmZpbHRlcmVkX2RpciwgIi1Ib21vX3NhcGllbnMuR1JDaDM4LjEwNC5zcGxpY2VkX2ludHJvbi50eG9tZS1zYWxpZ24tY3ItbGlrZS1lbSIpCgojIGdldCBwYXRoIHRvIGZyeSB1bmZpbHRlcmVkIHNrZXRjaCBvdXRwdXQgZGlyZWN0b3J5IApmcnlfc2tldGNoX2RpciA8LSBmaWxlLnBhdGgocXVhbnRzX2RpciwgImFsZXZpbi1mcnktdW5maWx0ZXJlZCIsIHNhbXBsZV9pZHMpCmZyeV9za2V0Y2hfZGlyIDwtIHBhc3RlMChmcnlfc2tldGNoX2RpciwgIi1Ib21vX3NhcGllbnMuR1JDaDM4LjEwNC5zcGxpY2VkX2ludHJvbi50eG9tZS1za2V0Y2gtY3ItbGlrZS1lbSIpCgojIHBhdGhzIHRvIHNwYXRpYWwgZm9sZGVycyAKY2VsbHJhbmdlcl9mb2xkZXJzIDwtIHBhc3RlMChzYW1wbGVfaWRzLCAiLUdSQ2gzOF8xMDRfY2VsbHJhbmdlcl9mdWxsLXNwYXRpYWwiKQpzcGFjZXJhbmdlcl9kaXIgPC0gZmlsZS5wYXRoKHF1YW50c19kaXIsICJjZWxscmFuZ2VyIiwgY2VsbHJhbmdlcl9mb2xkZXJzKQpgYGAKCgpgYGB7cn0KIyByZWFkIGluIGNvbWJpbmVkIGZyeSBhbmQgc3BhY2VyYW5nZXIgc3BlIGZvciBmcnkga25lZQpmcnlfa25lZV9zcGVfMSA8LSBjcmVhdGVfZnJ5X3NwYWNlcmFuZ2VyX3NwZShmcnlfa25lZV9kaXJbMV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGFjZXJhbmdlcl9kaXJbMV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfaWRzWzFdKQoKZnJ5X2tuZWVfc3BlXzIgPC0gY3JlYXRlX2ZyeV9zcGFjZXJhbmdlcl9zcGUoZnJ5X2tuZWVfZGlyWzJdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BhY2VyYW5nZXJfZGlyWzJdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2lkc1syXSkKYGBgCgpgYGB7cn0KIyByZWFkIGluIGNvbWJpbmVkIGZyeSBhbmQgc3BhY2VyYW5nZXIgc3BlIGZvciBmcnkgdW5maWx0ZXJlZApmcnlfdW5maWx0ZXJlZF9zcGVfMSA8LSBjcmVhdGVfZnJ5X3NwYWNlcmFuZ2VyX3NwZShmcnlfdW5maWx0ZXJlZF9kaXJbMV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGFjZXJhbmdlcl9kaXJbMV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfaWRzWzFdKQoKZnJ5X3VuZmlsdGVyZWRfc3BlXzIgPC0gY3JlYXRlX2ZyeV9zcGFjZXJhbmdlcl9zcGUoZnJ5X3VuZmlsdGVyZWRfZGlyWzJdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BhY2VyYW5nZXJfZGlyWzJdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2lkc1syXSkKYGBgCgpgYGB7cn0KIyByZWFkIGluIGNvbWJpbmVkIGZyeSBhbmQgc3BhY2VyYW5nZXIgc3BlIGZvciBmcnkga25lZQpmcnlfc2tldGNoX3NwZV8xIDwtIGNyZWF0ZV9mcnlfc3BhY2VyYW5nZXJfc3BlKGZyeV9za2V0Y2hfZGlyWzFdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BhY2VyYW5nZXJfZGlyWzFdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2lkc1sxXSkKCmZyeV9za2V0Y2hfc3BlXzIgPC0gY3JlYXRlX2ZyeV9zcGFjZXJhbmdlcl9zcGUoZnJ5X3NrZXRjaF9kaXJbMl0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGFjZXJhbmdlcl9kaXJbMl0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfaWRzWzJdKQpgYGAKCgpgYGB7cn0KIyBzcGFjZXJhbmdlciBvdXRwdXQgcGF0aHMKc3BhY2VyYW5nZXJfZGlyIDwtIGZpbGUucGF0aChxdWFudHNfZGlyLCAiY2VsbHJhbmdlciIsIGNlbGxyYW5nZXJfZm9sZGVycykKCiMgcmVhZCBpbiBzcGFjZXJhbmdlciBvdXRwdXQgZGlyZWN0bHkgdXNpbmcgcmVhZDEwWFZpc2l1bQpzcGFjZXJhbmdlcl9zcGVfMSA8LSByZWFkMTB4VmlzaXVtKGZpbGUucGF0aChzcGFjZXJhbmdlcl9kaXJbMV0sICJvdXRzIiksIHNhbXBsZV9pZCA9IHNhbXBsZV9pZHNbMV0pCnNwYWNlcmFuZ2VyX3NwZV8yIDwtIHJlYWQxMHhWaXNpdW0oZmlsZS5wYXRoKHNwYWNlcmFuZ2VyX2RpclsyXSwgIm91dHMiKSwgc2FtcGxlX2lkID0gc2FtcGxlX2lkc1syXSkKYGBgCgojIyMgUGVyIFNwb3QgUUMgTWV0cmljcwoKTm93IHRoYXQgd2UgaGF2ZSByZWFkIGluIHRoZSBkYXRhIGFuZCBjcmVhdGVkIG91ciB0d28gYFNwYXRpYWxFeHBlcmltZW50YCBvYmplY3RzLCB3ZSBjYW4gZ28gYWhlYWQgYW5kIGNvbWJpbmUgdGhlbSBpbnRvIG9uZSBsaXN0IGFuZCB0aGVuIGNhbGN1bGF0ZSB0aGUgcGVyIHNwb3QgUUMgbWV0cmljcyB1c2luZyBgc2N1dHRsZTo6YWRkUGVyQ2VsbFFDTWV0cmljcygpLmAKCmBgYHtyfQojIGNyZWF0ZSBvbmUgbGlzdCB3aXRoIGJvdGggc3BlJ3MgdG9nZXRoZXIKYWxsX3NwZV9saXN0IDwtIGxpc3QoZnJ5X2tuZWVfc3BlXzEsIGZyeV91bmZpbHRlcmVkX3NwZV8xLCAKICAgICAgICAgICAgICAgICAgICAgZnJ5X3NrZXRjaF9zcGVfMSwgc3BhY2VyYW5nZXJfc3BlXzEsIAogICAgICAgICAgICAgICAgICAgICBmcnlfa25lZV9zcGVfMiwgZnJ5X3VuZmlsdGVyZWRfc3BlXzIsIAogICAgICAgICAgICAgICAgICAgICBmcnlfc2tldGNoX3NwZV8yLCBzcGFjZXJhbmdlcl9zcGVfMikKCiMgbmFtZSBlYWNoIHNwZSB3aXRoIGNvbWJpbmF0aW9uIG9mIHNhbXBsZV9pZC10b29sIApzcGVfbmFtZXMgPC0gYygiU0NQQ1IwMDAzNzItYWxldmluLWZyeS1rbmVlIiwgIlNDUENSMDAwMzcyLWFsZXZpbi1mcnktdW5maWx0ZXJlZCIsIAogICAgICAgICAgICAgICAiU0NQQ1IwMDAzNzItYWxldmluLWZyeS11bmZpbHRlcmVkLXNrZXRjaCIsICJTQ1BDUjAwMDM3Mi1zcGFjZXJhbmdlciIsCiAgICAgICAgICAgICAgICJTQ1BDUjAwMDM3My1hbGV2aW4tZnJ5LWtuZWUiLCAiU0NQQ1IwMDAzNzMtYWxldmluLWZyeS11bmZpbHRlcmVkIiwgCiAgICAgICAgICAgICAgICJTQ1BDUjAwMDM3My1hbGV2aW4tZnJ5LXVuZmlsdGVyZWQtc2tldGNoIiwgIlNDUENSMDAwMzczLXNwYWNlcmFuZ2VyIikKbmFtZXMoYWxsX3NwZV9saXN0KSA8LSBzcGVfbmFtZXMKCiMgY2FsY3VsYXRlIHBlciBjZWxsIFFDIGFuZCBvdXRwdXQgdG8gYSBjb21iaW5lZCBkYXRhIGZyYW1lIHdpdGggcGxvdHRpbmcgCmFsbF9zcGVfbGlzdCA8LSBhbGxfc3BlX2xpc3QgJT4lCiAgICBwdXJycjo6bWFwKAogICAgICB+IHNjdXR0bGU6OmFkZFBlckNlbGxRQ01ldHJpY3MoLngsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0cyA9IGxpc3QobWl0byA9IG1pdG9fZ2VuZXNbbWl0b19nZW5lcyAlaW4lIHJvd25hbWVzKC54KV0pKSkKYGBgCgpBZnRlciBhZGRpbmcgaW4gdGhlIHBlciBzcG90IFFDIG1ldHJpY3MgdG8gYm90aCBvZiB0aGUgc3BlJ3MsIHdlIHdhbnQgdG8gZXh0cmFjdCB0aGUgYGNvbERhdGFgIGZyb20gZWFjaCBzcGUgYW5kIGNyZWF0ZSBhIGRhdGEgZnJhbWUgdGhhdCB3ZSBjYW4gdXNlIGZvciBwbG90dGluZy4gCldlIHdpbGwgYWxzbyBuZWVkIHNvbWUgaW5mb3JtYXRpb24gYWJvdXQgZWFjaCBzYW1wbGUgYW5kIGhvdyBpdCB3YXMgcnVuLCBzbyB3ZSB3aWxsIGNyZWF0ZSBhIHNhbXBsZSBtZXRhZGF0YSB0YWJsZSwgYHNhbXBsZV9pbmZvX2RmYCB0aGF0IHdpbGwgdGhlbiBiZSBtZXJnZWQgd2l0aCB0aGUgYGNvbERhdGFgLiAKCmBgYHtyfQojIGNyZWF0ZSBzYW1wbGUgaW5mbyBkYXRhZnJhbWUgdG8gYmUgam9pbmVkIHdpdGggcGVyIHNwb3QgZGF0YWZyYW1lIGxhdGVyCnNhbXBsZV9pbmZvX2RmIDwtIHF1YW50X2luZm9fdGFibGUoZGF0YV9kaXI9IHF1YW50c19kaXIsIAogICAgICAgICAgICAgICAgIHRvb2xzID0gYygiY2VsbHJhbmdlciIsICJhbGV2aW4tZnJ5LWtuZWUiLCAiYWxldmluLWZyeS11bmZpbHRlcmVkIiksCiAgICAgICAgICAgICAgICAgc2FtcGxlcyA9IHNhbXBsZV9pZHMpICU+JQogICMgY29udmVydCBjZWxscmFuZ2VyIHRvIHNwYWNlcmFuZ2VyIGFuZCBwYXN0ZSBmaWx0ZXJpbmcgc3RyYXRlZ3kgdG8gYWxldmluLWZyeQogIGRwbHlyOjptdXRhdGUodG9vbCA9IGlmZWxzZSh0b29sID09ICJjZWxscmFuZ2VyIiwgInNwYWNlcmFuZ2VyIiwgcGFzdGUodG9vbCwgZmlsdGVyX3N0cmF0ZWd5LCBzZXAgPSAiLSIpKSwKICAgICAgICAgICAgICAgIHRvb2wgPSBkcGx5cjo6Y2FzZV93aGVuKGFsZXZpbl9hbGlnbm1lbnQgPT0gInNrZXRjaCIgfiBwYXN0ZSh0b29sLCBhbGV2aW5fYWxpZ25tZW50LCBzZXAgPSAiLSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxldmluX2FsaWdubWVudCAhPSAic2tldGNoIiB+IHRvb2wpKQoKc2FtcGxlX2luZm9fZGYKYGBgCgpXaGVuIHdlIGNvbnZlcnQgdGhlIGBjb2xEYXRhYCB0byBhIGRhdGEgZnJhbWUgd2UgdXNlIHRoZSBjdXN0b20gZnVuY3Rpb24sIGBzcGF0aWFsX2NvbGRhdGFfdG9fZGYoKWAgdG8gZG8gc28gYW5kIGFwcGx5IGl0IHRvIGVhY2ggc3BlIGluIG91ciBsaXN0LiAKYGBge3J9CmZyeV9rbmVlX25hbWVzIDwtIGMoIlNDUENSMDAwMzcyLWFsZXZpbi1mcnkta25lZSIsICJTQ1BDUjAwMDM3My1hbGV2aW4tZnJ5LWtuZWUiKQpmcnlfdW5maWx0ZXJlZF9uYW1lcyA8LSBjKCJTQ1BDUjAwMDM3Mi1hbGV2aW4tZnJ5LXVuZmlsdGVyZWQiLCAiU0NQQ1IwMDAzNzMtYWxldmluLWZyeS11bmZpbHRlcmVkIikKZnJ5X3VuZmlsdGVyZWRfc2tldGNoX25hbWVzIDwtIGMoIlNDUENSMDAwMzcyLWFsZXZpbi1mcnktdW5maWx0ZXJlZC1za2V0Y2giLCAiU0NQQ1IwMDAzNzMtYWxldmluLWZyeS11bmZpbHRlcmVkLXNrZXRjaCIpCnNwYWNlcmFuZ2VyX25hbWVzIDwtIGMoIlNDUENSMDAwMzcyLXNwYWNlcmFuZ2VyIiwgIlNDUENSMDAwMzczLXNwYWNlcmFuZ2VyIikKCiMgam9pbiBjb2xkYXRhIGRhdGFmcmFtZSB3aXRoIHNhbXBsZSBpbmZvCmNvbGRhdGFfZGYgPC0gYWxsX3NwZV9saXN0ICU+JQogIHB1cnJyOjptYXBfZGYoc3BhdGlhbF9jb2xkYXRhX3RvX2RmLCAuaWQgPSAidG9vbCIpICU+JQogICMgcmVtb3ZlIGV4dHJhIC0xIGZyb20gc3BhY2VyYW5nZXIgYmFyY29kZXMKICBkcGx5cjo6bXV0YXRlKHNwb3RfaWQgPSBnc3ViKCItMSIsICIiLCBzcG90X2lkKSwKICAgICAgICAgICAgICAgICMgcmVtb3ZlIHRvb2wgZnJvbSBzYW1wbGUgaWQgCiAgICAgICAgICAgICAgICBzYW1wbGVfaWQgPSBzdHJpbmdyOjp3b3JkKHNhbXBsZV9pZCwgMSwgc2VwID0gIi0iKSwKICAgICAgICAgICAgICAgICMgcmVtb3ZlIHNhbXBsZSBpZCBmcm9tIHRvb2wgCiAgICAgICAgICAgICAgICB0b29sID0gZHBseXI6OmNhc2Vfd2hlbih0b29sICVpbiUgZnJ5X2tuZWVfbmFtZXMgfiAiYWxldmluLWZyeS1rbmVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvb2wgJWluJSBmcnlfdW5maWx0ZXJlZF9uYW1lcyB+ICJhbGV2aW4tZnJ5LXVuZmlsdGVyZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9vbCAlaW4lIGZyeV91bmZpbHRlcmVkX3NrZXRjaF9uYW1lcyB+ICJhbGV2aW4tZnJ5LXVuZmlsdGVyZWQtc2tldGNoIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b29sICVpbiUgc3BhY2VyYW5nZXJfbmFtZXMgfiAic3BhY2VyYW5nZXIiKSkgJT4lCiAgZHBseXI6OmxlZnRfam9pbihzYW1wbGVfaW5mb19kZiwKICAgICAgICAgICAgICAgICAgIGJ5ID0gYygidG9vbCIsICJzYW1wbGVfaWQiID0gInNhbXBsZSIpKSAlPiUKICAjIHJlbW92ZSBzcG90cyB0aGF0IGFyZSBub3Qgb3ZlcmxhcHBpbmcgdGlzc3VlIAogIGRwbHlyOjpmaWx0ZXIoaW5fdGlzc3VlID09IDEpCmBgYAoKTm93IHdlIG9ubHkgd2FudCB0byBmaWx0ZXIgb3VyIGRhdGEgZnJhbWUgdG8gY29udGFpbiBzcG90cyB0aGF0IGFyZSBzaGFyZWQgYmV0d2VlbiBib3RoIHRvb2xzLiAKCmBgYHtyfQojIGlkZW50aWZ5IHNoYXJlZCBzcG90cyBvbmx5IApzcG90X2NvdW50cyA8LSBjb2xkYXRhX2RmICU+JQogIGRwbHlyOjpjb3VudChzcG90X2lkLCBzYW1wbGVfaWQpCmBgYAoKYGBge3J9CiMgaG93IG1hbnkgc3BvdHMgYXJlIHNoYXJlZCBhbW9uZyB0aGUgdG9vbHMKc3BvdF9jb3VudHNfcGxvdCA8LSBjb2xkYXRhX2RmICU+JQogIGRwbHlyOjpncm91cF9ieShzcG90X2lkLCBzYW1wbGVfaWQpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UodG9vbHNfZGV0ZWN0ZWQgPSBsaXN0KHVuaXF1ZSh0b29sKSkpCgpnZ3Bsb3Qoc3BvdF9jb3VudHNfcGxvdCwgYWVzKHggPSB0b29sc19kZXRlY3RlZCkpKwogIGdlb21fYmFyKCkgKwogIHNjYWxlX3hfdXBzZXQobl9pbnRlcnNlY3Rpb25zID0gNCkKYGBgCkZvciB0aGUgbW9zdCBwYXJ0LCB0aGUgbWFqb3JpdHkgb2YgdGhlIHNwb3RzIGlkZW50aWZpZWQgYXJlIGZvdW5kIGluIGJvdGggU3BhY2VyYW5nZXIgYWxvbmUgYW5kIHRoZSBjb21iaW5hdGlvbiB3aXRoIEFsZXZpbi1mcnkta25lZSBhbmQgQWxldmluLWZyeS11bmZpbHRlcmVkLCB3aXRoIGEgc21hbGwgc3Vic2V0IGJlaW5nIGlkZW50aWZpZWQgaW4gU3BhY2VyYW5nZXIgYW5kIEFsZXZpbi1mcnktdW5maWx0ZXJlZC4KSXQgYXBwZWFycyB0aGF0IHVzaW5nIGFsZXZpbi1mcnktdW5maWx0ZXJlZCBkb2VzIGdpdmUgdXMgc29tZSBzcG90cyB0aGF0IHVzaW5nIHRoZSBrbmVlIG1ldGhvZCBkb2VzIG5vdCBnaXZlIHVzIGFuZCB3ZSBkb24ndCBzZWUgYW55IGxvc3Mgb2Ygc3BvdHMuIAoKTGV0J3MgZmlsdGVyIHRvIG9ubHkgaW5jbHVkZSB0aGVzZSBjb21tb24gc3BvdHMuCgpgYGB7cn0KY29tbW9uX3Nwb3RzIDwtIHNwb3RfY291bnRzICU+JQogIGRwbHlyOjpmaWx0ZXIobiA9PSA0KSAlPiUKICBkcGx5cjo6cHVsbChzcG90X2lkKQoKY29sZGF0YV9kZl9jb21tb24gPC0gY29sZGF0YV9kZiAlPiUKICBkcGx5cjo6ZmlsdGVyKHNwb3RfaWQgJWluJSBjb21tb25fc3BvdHMpCmBgYAoKV2Ugd2lsbCBhbHNvIG5lZWQgdG8gZmlsdGVyIHRoZSBzcGUncyBkaXJlY3RseSBiYXNlZCBvbiBzcG90cyB0aGF0IGFyZSBwcmVzZW50IGluIHRoZSB0aXNzdWUsIHNvIHdlIGNyZWF0ZSBhIHNtYWxsIGZ1bmN0aW9uIHRvIGRvIHRoaXMgYW5kIHRoZW4gYXBwbHkgaXQgdG8gYm90aCBzcGUncyBpbiB0aGUgbGlzdC4gCgpgYGB7cn0KIyB3ZSB3aWxsIGFsc28gd2FudCB0byBmaWx0ZXIgdGhlIHNwZSdzIGRpcmVjdGx5IApmaWx0ZXJfc3BlIDwtIGZ1bmN0aW9uKHNwZSl7CiAgc3BlIDwtIHNwZVssIHNwYXRpYWxEYXRhKHNwZSkkaW5fdGlzc3VlID09IDFdCn0KCmFsbF9zcGVfZmlsdGVyIDwtIGFsbF9zcGVfbGlzdCAlPiUKICBwdXJycjo6bWFwKGZpbHRlcl9zcGUpCmBgYAoKV2hlbiB3ZSBsb29rIGF0IG91ciByZXN1bHRzLCB3ZSB3aWxsIGFsc28gd2FudCB0byB2aXN1YWxpemUgdGhlbSBzbyB3ZSB3aWxsIG1ha2UgYSBjdXN0b20gZnVuY3Rpb24gdG8gcGxvdCB0aGUgcmVzdWx0cy4KYGBge3J9CiMgY3VzdG9tIGZ1bmN0aW9uIGZvciBwbG90dGluZyBzcGUgcmVzdWx0cyBhbmQgY29sb3JpbmcgYnkgY29sdW1uIG9mIGNvbERhdGEgb2YgY2hvaWNlCnBsb3Rfc3BlIDwtIGZ1bmN0aW9uKHNwZSwgc2FtcGxlLCBjb2x1bW4pewogICMgcGxvdCBzcG90cyBvbmx5IAogIHAxIDwtIGdnc3BhdmlzOjpwbG90U3BvdHMoc3BlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhfY29vcmQgPSAicHhsX2NvbF9pbl9mdWxscmVzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB5X2Nvb3JkID0gInB4bF9yb3dfaW5fZnVsbHJlcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGUgPSBjb2x1bW4pICsKICAgIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpCiAgCiAgIyBwbG90IHdpdGggdGlzc3VlIHVuZGVybmVhdGgKICBwMiA8LSBnZ3NwYXZpczo6cGxvdFZpc2l1bShzcGUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhfY29vcmQgPSAicHhsX2NvbF9pbl9mdWxscmVzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeV9jb29yZCA9ICJweGxfcm93X2luX2Z1bGxyZXMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gY29sdW1uKSArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpCiAgCiAgIyBhcnJhbmdlIHBsb3RzIGFuZCBhZGQgc2FtcGxlIG5hbWUgYXMgdGl0bGUgCiAgZ3JpZC5hcnJhbmdlKHAxLCBwMiwgbnJvdyA9IDEsIHRvcCA9IGdyaWQ6OnRleHRHcm9iKHNhbXBsZSkpCn0KYGBgCgpGaXJzdCB3ZSB3aWxsIGxvb2sgYXQgdGhlIHBlciBjZWxsIG1ldHJpY3M6IG1pdG9jaG9uZHJpYWwgcmVhZHMgcGVyIGNlbGwsIHRvdGFsIFVNSSBwZXIgY2VsbCwgYW5kIHRvdGFsIGdlbmVzIGRldGVjdGVkIHBlciBjZWxsLiAKCmBgYHtyfQojICUgbWl0b2Nob25kcmlhbCByZWFkcy8gc3BvdCAKZ2dwbG90KGNvbGRhdGFfZGZfY29tbW9uLCBhZXMoeCA9IHRvb2wsIHkgPSBzdWJzZXRzX21pdG9fcGVyY2VudCwgZmlsbCA9IHRvb2wpKSArIAogIGdlb21fYm94cGxvdCgpICsKICBmYWNldF93cmFwKH4gc2FtcGxlX2lkKSArCiAgdGhlbWVfY2xhc3NpYygpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKyAKICB5bGFiKCJNaXRvIFBlcmNlbnQiKSArIAogIHhsYWIoIiIpCmBgYAoKYGBge3J9CmFsbF9zcGVfZmlsdGVyICU+JQogIHB1cnJyOjppd2FsayhwbG90X3NwZSwgY29sdW1uID0gInN1YnNldHNfbWl0b19wZXJjZW50IikKYGBgCk92ZXJhbGwgaXQgbG9va3MgbGlrZSBtaXRvY2hvbmRyaWFsIGNvbnRlbnQgaXMgbG93IGFuZCBmYWlybHkgc2ltaWxhciBhY3Jvc3MgYm90aCB0b29scy4gCgpgYGB7cn0KIyB0b3RhbCBVTUkvIHNwb3QgCmdncGxvdChjb2xkYXRhX2RmX2NvbW1vbiwgYWVzKHggPSBzdW0sIGNvbG9yID0gdG9vbCkpICsgCiAgZ2VvbV9kZW5zaXR5KCkgKyAKICBmYWNldF93cmFwKH4gc2FtcGxlX2lkKSArCiAgdGhlbWVfY2xhc3NpYygpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKyAKICB5bGFiKCJVTUkvc3BvdCIpICsgCiAgeGxhYigiIikKYGBgCgpgYGB7cn0KYWxsX3NwZV9maWx0ZXIgJT4lCiAgcHVycnI6Oml3YWxrKHBsb3Rfc3BlLCBjb2x1bW4gPSAic3VtIikKYGBgCgoKYGBge3J9CiMgdG90YWwgZ2VuZXMvIHNwb3QgCmdncGxvdChjb2xkYXRhX2RmX2NvbW1vbiwgYWVzKHggPSBkZXRlY3RlZCwgY29sb3IgPSB0b29sKSkgKyAKICBnZW9tX2RlbnNpdHkoKSArCiAgZmFjZXRfd3JhcCh+IHNhbXBsZV9pZCkgKwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsgCiAgeWxhYigiR2VuZXMgZGV0ZWN0ZWQvc3BvdCIpICsgCiAgeGxhYigiIikgCmBgYAoKYGBge3J9CmFsbF9zcGVfZmlsdGVyICU+JQogIHB1cnJyOjppd2FsayhwbG90X3NwZSwgY29sdW1uID0gImRldGVjdGVkIikKYGBgCgpHZW5lcmFsbHkgaXQgbG9va3MgbGlrZSB0aGUgdG9vbHMgYXJlIGZhaXJseSBzaW1pbGFyLCBleGNlcHQgdGhhdCBBbGV2aW4tZnJ5IHNob3dzIGEgc2xpZ2h0IGRlY3JlYXNlIGluIGJvdGggdG90YWwgVU1JL2NlbGwgYW5kIGdlbmVzIGRldGVjdGVkL2NlbGwgd2hlbiBjb21wYXJlZCB0byBTcGFjZXJhbmdlciBhbG9uZSwgd2hpY2ggaXMgc2VlbiBpbiB0aGUgc3BhdGlhbCBwbG90IGFzIHdlbGwuIApBbGV2aW4tZnJ5LWtuZWUgYW5kIGFsZXZpbi1mcnktdW5maWx0ZXJlZCBzZWVtIHRvIGFsbW9zdCBjb21wbGV0ZWx5IG92ZXJsYXAgaW4gdGVybXMgb2YgcXVhbnRpZmljYXRpb24gd2l0aCB0aGUgb25seSBkaWZmZXJlbmNlIGJlaW5nIGEgZmV3IHNwb3RzIHRoYXQgd2VyZSBub3QgZGV0ZWN0ZWQgdXNpbmcgdGhlIGtuZWUgbWV0aG9kIGFyZSBub3cgaWRlbnRpZmllZCBpbiB0aGUgdW5maWx0ZXJlZCBtZXRob2QuIApJZiB1c2luZyBhbGV2aW4tZnJ5IHdpdGggYC0tc2tldGNoYCB3ZSBzZWUgdGhhdCB0aGUgVU1JL2NlbGwgYW5kIGdlbmVzIGRldGVjdGVkL2NlbGwgYWxtb3N0IGNvbXBsZXRlbHkgb3ZlcmxhcCB0aGUgY291bnRzIG9ic2VydmVkIGluIFNwYWNlcmFuZ2VyLiAKVGhpcyBpcyBhbHNvIHNlZW4gaW4gdGhlIHBsb3RzLCBhcyB0aGUgcGxvdHMgdmlzdWFsbHkgbG9vayBhbG1vc3QgaWRlbnRpY2FsIHdoZW4gdXNpbmcgYC0tc2tldGNoYCBidXQgbm90IHNlbGVjdGl2ZSBhbGlnbm1lbnQuIAoKCiMjIyBQZXIgR2VuZSBRQyBNZXRyaWNzCgpMZXQncyBhbHNvIGxvb2sgYXQgdGhlIGNvcnJlbGF0aW9uIG9mIG1lYW4gZ2VuZSBleHByZXNzaW9uIGFjcm9zcyBzaGFyZWQgZ2VuZXMuIApXZSB3aWxsIGZpcnN0IG5lZWQgdG8gY2FsY3VsYXRlIHRoZSBwZXIgZmVhdHVyZSBRQyBvbiB0aGUgZmlsdGVyZWQgc3BlcyBhZnRlciByZW1vdmluZyBzcG90cyBub3QgcHJlc2VudCBpbiB0aGUgdGlzc3VlIGFuZCB0aGVuIGdyYWIgdGhlIGByb3dEYXRhYCBhbmQgY29tYmluZSBpbnRvIGEgZGF0YSBmcmFtZSB1c2VkIGZvciBwbG90dGluZy4gCgpgYGB7cn0KYWxsX3NwZV9maWx0ZXIgPC0gYWxsX3NwZV9maWx0ZXIgJT4lCiAgcHVycnI6Om1hcChzY3V0dGxlOjphZGRQZXJGZWF0dXJlUUNNZXRyaWNzKQpgYGAKCmBgYHtyfQojIGdyYWIgcm93ZGF0YSBhbmQgY29tYmluZSB3aXRoIHNhbXBsZSBpbmZvCnJvd2RhdGFfZGYgPC0gcHVycnI6Om1hcF9kZihhbGxfc3BlX2ZpbHRlciwgc2NwY2FUb29sczo6cm93ZGF0YV90b19kZiwgLmlkID0gInRvb2wiKSAlPiUKICAjIGV4dHJhY3Qgc2FtcGxlX2lkIGZyb20gdG9vbCBhbmQgY3JlYXRlIGEgbmV3IGNvbHVtbiB0byBhdm9pZCBkdXBsaWNhdGVzCiAgZHBseXI6Om11dGF0ZShzYW1wbGVfaWQgPSBzdHJpbmdyOjp3b3JkKHRvb2wsIDEsIHNlcCA9ICItIiksCiAgICAgICAgICAgICAgICB0b29sID0gZHBseXI6OmNhc2Vfd2hlbih0b29sICVpbiUgZnJ5X2tuZWVfbmFtZXMgfiAiYWxldmluLWZyeS1rbmVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvb2wgJWluJSBmcnlfdW5maWx0ZXJlZF9uYW1lcyB+ICJhbGV2aW4tZnJ5LXVuZmlsdGVyZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9vbCAlaW4lIGZyeV91bmZpbHRlcmVkX3NrZXRjaF9uYW1lcyB+ICJhbGV2aW4tZnJ5LXVuZmlsdGVyZWQtc2tldGNoIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b29sICVpbiUgc3BhY2VyYW5nZXJfbmFtZXMgfiAic3BhY2VyYW5nZXIiKSkgJT4lCiAgZHBseXI6OmxlZnRfam9pbihzYW1wbGVfaW5mb19kZiwKICAgICAgICAgICAgICAgICAgIGJ5ID0gYygidG9vbCIsICJzYW1wbGVfaWQiID0gInNhbXBsZSIpKQpgYGAKCldlIHRoZW4gd2FudCB0byBmaWx0ZXIgb3V0IGFueSBsb3dseSBkZXRlY3RlZCBnZW5lcywgKGRldGVjdGVkIDwgNS4wKSBhbmQgcmVzdHJpY3Qgb3VyIGFuYWx5c2lzIHRvIHRob3NlIGdlbmVzIHRoYXQgYXJlIGZvdW5kIGluIGJvdGggdG9vbHMuIAoKYGBge3J9CmdlbmVfY291bnRzIDwtIHJvd2RhdGFfZGYgJT4lIAogICMgcmVtb3ZlIGdlbmVzIHRoYXQgaGF2ZSBhIGxvdyBmcmVxdWVuY3kgb2YgYmVpbmcgZGV0ZWN0ZWQKICBkcGx5cjo6ZmlsdGVyKGRldGVjdGVkID49IDUuMCkgJT4lCiAgZHBseXI6OmNvdW50KGdlbmVfaWQsIHNhbXBsZV9pZCkKCiMgcmVzdHJpY3QgdG8gb25seSBjb21tb24gZ2VuZXMgCmNvbW1vbl9nZW5lcyA8LSBnZW5lX2NvdW50cyAlPiUKICBkcGx5cjo6ZmlsdGVyKG4gPT0gNCkgJT4lCiAgZHBseXI6OnB1bGwoZ2VuZV9pZCkKCnJvd2RhdGFfZGZfY29tbW9uIDwtIHJvd2RhdGFfZGYgJT4lCiAgZHBseXI6OmZpbHRlcihnZW5lX2lkICVpbiUgY29tbW9uX2dlbmVzKSAKYGBgCgpgYGB7cn0KIyBjcmVhdGUgYSB0YWJsZSB0byBjYWxjdWxhdGUgY29ycmVsYXRpb24gYmV0d2VlbiBtZWFuIGdlbmUgZXhwcmVzc2lvbgpyb3dkYXRhX2NvciA8LSByb3dkYXRhX2RmX2NvbW1vbiAlPiUKICBkcGx5cjo6c2VsZWN0KHRvb2wsIGdlbmVfaWQsIHNhbXBsZV9pZCwgbWVhbikgJT4lCiAgIyBzcHJlYWQgdGhlIG1lYW4gZXhwcmVzc2lvbiBzdGF0cyB0byBvbmUgY29sdW1uIHBlciBjYWxsZXIKICB0aWR5cjo6cGl2b3Rfd2lkZXIoaWRfY29scyA9IGMoZ2VuZV9pZCwgc2FtcGxlX2lkKSwKICAgICAgICAgICAgICAgICAgICAgbmFtZXNfZnJvbSA9IGMoInRvb2wiKSwKICAgICAgICAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSBtZWFuKSAlPiUKICAjIGRyb3Agcm93cyB3aXRoIE5BIHZhbHVlcyB0byBlYXNlIGNvcnJlbGF0aW9uIGNhbGN1bGF0aW9uCiAgdGlkeXI6OmRyb3BfbmEoKQpgYGAKCmBgYHtyfQojIGxvb2sgYXQgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgdHdvIHRvb2xzCnJvd2RhdGFfY29yICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoc2FtcGxlX2lkKSAlPiUKICBkcGx5cjo6c3VtbWFyaXplKAogICAgYWxldmluX2ZyeV9rbmVlX3NwYWNlcmFuZ2VyX2NvciA9IGNvcihgc3BhY2VyYW5nZXJgLCBgYWxldmluLWZyeS1rbmVlYCwgbWV0aG9kID0gInNwZWFybWFuIiksCiAgICBhbGV2aW5fZnJ5X3VuZmlsdGVyZWRfc3BhY2VyYW5nZXJfY29yPSBjb3IoYHNwYWNlcmFuZ2VyYCwgYGFsZXZpbi1mcnktdW5maWx0ZXJlZGAsIG1ldGhvZCA9ICJzcGVhcm1hbiIpLAogICAgYWxldmluX2ZyeV91bmZpbHRlcmVkX3NrZXRjaF9zcGFjZXJhbmdlcl9jb3IgPSBjb3IoYHNwYWNlcmFuZ2VyYCwgYGFsZXZpbi1mcnktdW5maWx0ZXJlZC1za2V0Y2hgLCBtZXRob2QgPSAic3BlYXJtYW4iKQogICkKYGBgCgpgYGB7cn0KIyBtZWFuIGdlbmUgZXhwcmVzc2lvbiBhY3Jvc3Mgc2hhcmVkIGdlbmVzIApnZ3Bsb3Qocm93ZGF0YV9jb3IsIGFlcyh4ID0gYHNwYWNlcmFuZ2VyYCwgeSA9IGBhbGV2aW4tZnJ5LWtuZWVgKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgYWxwaGEgPSAwLjEpICsgCiAgc2NhbGVfeF9sb2cxMCgpICsgCiAgc2NhbGVfeV9sb2cxMCgpICsgCiAgZmFjZXRfd3JhcCh+c2FtcGxlX2lkKSArCiAgZ2VvbV9hYmxpbmUoKSArCiAgbGFicyh4ID0gIlNwYWNlcmFuZ2VyIG1lYW4gZ2VuZSBleHByZXNzaW9uIiwgeSA9ICJBbGV2aW4gRnJ5IEtuZWUgTWVhbiBnZW5lIGV4cHJlc3Npb24iKSArIAogIHRoZW1lX2NsYXNzaWMoKQogIApgYGAKCmBgYHtyfQpnZ3Bsb3Qocm93ZGF0YV9jb3IsIGFlcyh4ID0gYHNwYWNlcmFuZ2VyYCwgeSA9IGBhbGV2aW4tZnJ5LXVuZmlsdGVyZWRgKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgYWxwaGEgPSAwLjEpICsgCiAgc2NhbGVfeF9sb2cxMCgpICsgCiAgc2NhbGVfeV9sb2cxMCgpICsgCiAgZmFjZXRfd3JhcCh+c2FtcGxlX2lkKSArCiAgZ2VvbV9hYmxpbmUoKSArCiAgbGFicyh4ID0gIlNwYWNlcmFuZ2VyIG1lYW4gZ2VuZSBleHByZXNzaW9uIiwgeSA9ICJBbGV2aW4gRnJ5IFVuZmlsdGVyZWQgTWVhbiBnZW5lIGV4cHJlc3Npb24iKSArIAogIHRoZW1lX2NsYXNzaWMoKQpgYGAKYGBge3J9CmdncGxvdChyb3dkYXRhX2NvciwgYWVzKHggPSBgc3BhY2VyYW5nZXJgLCB5ID0gYGFsZXZpbi1mcnktdW5maWx0ZXJlZC1za2V0Y2hgKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgYWxwaGEgPSAwLjEpICsgCiAgc2NhbGVfeF9sb2cxMCgpICsgCiAgc2NhbGVfeV9sb2cxMCgpICsgCiAgZmFjZXRfd3JhcCh+c2FtcGxlX2lkKSArCiAgZ2VvbV9hYmxpbmUoKSArCiAgbGFicyh4ID0gIlNwYWNlcmFuZ2VyIG1lYW4gZ2VuZSBleHByZXNzaW9uIiwgeSA9ICJBbGV2aW4gRnJ5IFVuZmlsdGVyZWQgU2tldGNoIE1lYW4gZ2VuZSBleHByZXNzaW9uIikgKyAKICB0aGVtZV9jbGFzc2ljKCkKYGBgCkNvcnJlbGF0aW9uIGFwcGVhcnMgdG8gYmUgcXVpdGUgaGlnaCBiZXR3ZWVuIG1lYW4gZ2VuZSBleHByZXNzaW9uIGluIFNwYWNlcmFuZ2VyIGFuZCBBbGV2aW4tZnJ5LCBob3dldmVyLCB3ZSBkbyBzZWUgdGhhdCBnZW5lcmFsbHkgZ2VuZXMgaGF2ZSBoaWdoZXIgZ2VuZSBleHByZXNzaW9uIGluIFNwYWNlcmFuZ2VyIHRoYW4gaW4gQWxldmluLWZyeSBhbmQgYXJlIHNsaWdodGx5IG9mZiB0aGUgZGlhZ29uYWwuIAoKSW4gYWxldmluLWZyeSBgLS1za2V0Y2hgIHRoZSBnZW5lcyBsaW5lIHVwIGFsb25nIHRoZSB4PXkgYXhpcyBhbmQgYXBwZWFyIHRvIGhhdmUgbGVzcyBvZiBhbiBpbmNyZWFzZSBpbiBleHByZXNzaW9uIHdpdGggU3BhY2VyYW5nZXIgcXVhbnRpZmljYXRpb24sIGhvd2V2ZXIgdGhlIGdyb3VwIG9mIGdlbmVzIHRoYXQgYXJlIHNsaWdodGx5IG9mZiB0aGUgZGlhZ29uYWwgaXMgc3RpbGwgcHJlc2VudC4gCgpUaGVyZSBpcyBhIHN1YnNldCBvZiBnZW5lcyB0aGF0IHNlZW1zIHRvIGJlIHNsaWdodGx5IG1vcmUgYWZmZWN0ZWQgYW5kIGJlIGZ1cnRoZXIgb2ZmIHRoZSBkaWFnb25hbC4gCkxldCdzIHRha2UgYXQgdGhvc2UgZ2VuZSBmb3IgZWFjaCBzYW1wbGUuIApJdCBkb2VzIGFwcGVhciB0byBiZSBsZXNzIGRyYW1hdGljIGluIFNDUENSMDAwMzczLgpXZSB3aWxsIG9ubHkgZm9jdXMgb24gd2l0aG91dCBgLS1za2V0Y2hgLCBhcyB0aGF0IGlzIHdoZXJlIHdlIHNlZSB0aGUgbGFyZ2VzdCBpbm1wYWN0IG9uIGdlbmUgZXhwcmVzc2lvbiBkaWZmZXJlbmNlcy4gCgpgYGB7cn0KIyBnZXQgdGhlIGdlbmUgc3ltYm9scyBhbmQgdGhlbiBqb2luIGJhY2sgd2l0aCByb3dkYXRhIGRmIApnZW5lX3N5bWJvbHNfZGYgPC0gcm93ZGF0YV9kZiAlPiUKICBkcGx5cjo6c2VsZWN0KGdlbmVfaWQsIHN5bWJvbCkKCiMgam9pbiBjb3JyZWxhdGlvbiB3aXRoIGdlbmUgc3ltYm9scyAKcm93ZGF0YV9jb3IgPC0gcm93ZGF0YV9jb3IgJT4lCiAgZHBseXI6OmxlZnRfam9pbihnZW5lX3N5bWJvbHNfZGYpICU+JQogIGRwbHlyOjpkaXN0aW5jdCgpICU+JQogIHRpZHlyOjpkcm9wX25hKCkgJT4lCiAgIyBhZGQgZGlmZmVyZW5jZSBpbiBtZWFuIGdlbmUgZXhwcmVzc2lvbiBiZXR3ZWVuIGFsZXZpbi1mcnkgYW5kIHNwYWNlcmFuZ2VyIAogIGRwbHlyOjptdXRhdGUoa25lZV9sb2dfZm9sZF9jaGFuZ2UgPSBsb2coYGFsZXZpbi1mcnkta25lZWAvc3BhY2VyYW5nZXIpLAogICAgICAgICAgICAgICAgdW5maWx0ZXJlZF9sb2dfZm9sZF9jaGFuZ2UgPSBsb2coYGFsZXZpbi1mcnktdW5maWx0ZXJlZGAvYHNwYWNlcmFuZ2VyYCkpCmBgYAoKCkxldCdzIHNlZSBpZiB3ZSBjYW4gc3BlY2lmaWNhbGwgaWRlbnRpZnkgdGhlIGdyb3VwIG9mIGdlbmVzIHRoYXQgYXJlIG9mZiB0aGUgZGlhZ29uYWwgYnkgbGFiZWxpbmcgdGhlbSB3aXRoIGEgZGlmZmVyZW50IGNvbG9yLiAKCmBgYHtyfQpyb3dkYXRhX2NvciA8LSByb3dkYXRhX2NvciAlPiUKICBkcGx5cjo6bXV0YXRlKGtuZWVfZGlmZiA9IGlmZWxzZSgKICAgIGtuZWVfbG9nX2ZvbGRfY2hhbmdlIDwgLSAwLjc1ICYgCiAgICAgIHNwYWNlcmFuZ2VyIDwgNSAmIAogICAgICBzcGFjZXJhbmdlciA+IC0xLCAiZGlmZl9leHByZXNzaW9uIiwgImVxdWFsX2V4cHJlc3Npb24iKSwKICAgIHVuZmlsdGVyZWRfZGlmZiA9IGlmZWxzZSgKICAgIHVuZmlsdGVyZWRfbG9nX2ZvbGRfY2hhbmdlIDwgLSAwLjc1ICYgCiAgICAgIHNwYWNlcmFuZ2VyIDwgNSAmIAogICAgICBzcGFjZXJhbmdlciA+IC0xLCAiZGlmZl9leHByZXNzaW9uIiwgImVxdWFsX2V4cHJlc3Npb24iKSkKYGBgCgoKYGBge3J9CmdncGxvdChyb3dkYXRhX2NvciwgYWVzKHggPSBgc3BhY2VyYW5nZXJgLCB5ID0gYGFsZXZpbi1mcnkta25lZWAsIGNvbG9yID0ga25lZV9kaWZmKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgYWxwaGEgPSAwLjEpICsgCiAgc2NhbGVfeF9sb2cxMCgpICsgCiAgc2NhbGVfeV9sb2cxMCgpICsgCiAgZ2VvbV9hYmxpbmUoKSArCiAgZmFjZXRfd3JhcCh+IHNhbXBsZV9pZCwgbnJvdyA9IDIpICsgCiAgbGFicyh4ID0gIlNwYWNlcmFuZ2VyIG1lYW4gZ2VuZSBleHByZXNzaW9uIiwgeSA9ICJBbGV2aW4gRnJ5IEtuZWUgTWVhbiBnZW5lIGV4cHJlc3Npb24iKSArIAogIHRoZW1lX2NsYXNzaWMoKQpgYGAKYGBge3J9CmdncGxvdChyb3dkYXRhX2NvciwgYWVzKHggPSBgc3BhY2VyYW5nZXJgLCB5ID0gYGFsZXZpbi1mcnktdW5maWx0ZXJlZGAsIGNvbG9yID0gdW5maWx0ZXJlZF9kaWZmKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgYWxwaGEgPSAwLjEpICsgCiAgc2NhbGVfeF9sb2cxMCgpICsgCiAgc2NhbGVfeV9sb2cxMCgpICsgCiAgZ2VvbV9hYmxpbmUoKSArCiAgZmFjZXRfd3JhcCh+IHNhbXBsZV9pZCwgbnJvdyA9IDIpICsgCiAgbGFicyh4ID0gIlNwYWNlcmFuZ2VyIG1lYW4gZ2VuZSBleHByZXNzaW9uIiwgeSA9ICJBbGV2aW4gRnJ5IFVuZmlsdGVyZWQgTWVhbiBnZW5lIGV4cHJlc3Npb24iKSArIAogIHRoZW1lX2NsYXNzaWMoKQpgYGAKCkl0IGxvb2tzIGxpa2UgdGhlIGdlbmVzIHRoYXQgdGhpcyBzZXBhcmF0ZSBncm91cCBvZiBnZW5lcyBoYXZlIGEgbG9nKGZvbGQgY2hhbmdlKSA8IC0wLjc1IGFuZCBtZWFuIGdlbmUgZXhwcmVzc2lvbiBpbiBzcGFjZXJhbmdlciA8IDUgYW5kID4gLTEuIApMZXQncyBnZXQgdGhhdCBsaXN0IG9mIGdlbmVzIGluIGJvdGggYWxldmluLWZyeS1rbmVlIGFuZCBhbGV2aW4tZnJ5LXVuZmlsdGVyZWQuIAoKYGBge3J9CmtuZWVfZGlmZl9nZW5lX2NvdW50cyA8LSByb3dkYXRhX2NvciAlPiUKICBkcGx5cjo6ZmlsdGVyKGtuZWVfZGlmZiA9PSAiZGlmZl9leHByZXNzaW9uIikgJT4lCiAgZHBseXI6OmNvdW50KGdlbmVfaWQpICU+JSAKICBkcGx5cjo6ZmlsdGVyKG4gPT0gMikgJT4lCiAgZHBseXI6OnB1bGwoZ2VuZV9pZCkKCmtuZWVfZGlmZl9nZW5lcyA8LSByb3dkYXRhX2NvciAlPiUKICBkcGx5cjo6ZmlsdGVyKGdlbmVfaWQgJWluJSBrbmVlX2RpZmZfZ2VuZV9jb3VudHMpICU+JQogIGRwbHlyOjphcnJhbmdlKHN5bWJvbCkKCmtuZWVfZGlmZl9nZW5lcwpgYGAKYGBge3J9CnVuZmlsdGVyZWRfZGlmZl9nZW5lX2NvdW50cyA8LSByb3dkYXRhX2NvciAlPiUKICBkcGx5cjo6ZmlsdGVyKHVuZmlsdGVyZWRfZGlmZiA9PSAiZGlmZl9leHByZXNzaW9uIikgJT4lCiAgZHBseXI6OmNvdW50KGdlbmVfaWQpICU+JSAKICBkcGx5cjo6ZmlsdGVyKG4gPT0gMikgJT4lCiAgZHBseXI6OnB1bGwoZ2VuZV9pZCkKCnVuZmlsdGVyZWRfZGlmZl9nZW5lcyA8LSByb3dkYXRhX2NvciAlPiUKICBkcGx5cjo6ZmlsdGVyKGdlbmVfaWQgJWluJSB1bmZpbHRlcmVkX2RpZmZfZ2VuZV9jb3VudHMpICU+JQogIGRwbHlyOjphcnJhbmdlKHN5bWJvbCkKCnVuZmlsdGVyZWRfZGlmZl9nZW5lcwpgYGAKTGV0J3MgbG9vayBhdCB3aGF0IHR5cGVzIG9mIGdlbmVzIGFyZSBmb3VuZCB0byBoYXZlIGRpZmZlcmVudCBnZW5lIGV4cHJlc3Npb24gYWNyb3NzIHRoZXNlIHR3byB0b29scyB1c2luZyBvdmVyIHJlcHJlc2VudGF0aW9uIGFuYWx5c2lzLiAKRmlyc3QsIGxldCdzIGxvb2sgc3BlY2lmaWNhbGx5IGF0IHRoYXQgZ3JvdXAgb2YgZ2VuZXMgdGhhdCBpcyBzbGlnaHRseSBvZmYgdGhlIGRpYWdvbmFsIChjb25zaWRlcmluZyBvbmx5IGdlbmVzIGZvdW5kIGluIGJvdGggc2FtcGxlcyksIHRoZW4gd2Ugd2lsbCBsb29rIGF0IGFsbCBnZW5lcyB3aXRoIGFicyhsb2coZm9sZCBjaGFuZ2UpID4gMC41KQoKYGBge3J9CiMgdW5maWx0ZXJlZCBhbmQga25lZSBvdXRsaWVyIGdlbmUgbGlzdHMKa25lZV9kaWZmX2dlbmVzIDwtIGtuZWVfZGlmZl9nZW5lcyRzeW1ib2wgJT4lCiAgdW5pcXVlKCkKCnVuZmlsdGVyZWRfZGlmZl9nZW5lcyA8LSB1bmZpbHRlcmVkX2RpZmZfZ2VuZXMkc3ltYm9sICU+JQogIHVuaXF1ZSgpCgojIGJhY2tncm91bmQgZ2VuZSBsaXN0IApiYWNrZ3JvdW5kX2dlbmVzIDwtIHJvd2RhdGFfY29yJHN5bWJvbCAlPiUKICB1bmlxdWUoKQpgYGAKCmBgYHtyfQprbmVlX2dvX29yYV9yZXN1bHRzIDwtIGVucmljaEdPKGdlbmUgPSBrbmVlX291dGxpZXJfZ2VuZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdW5pdmVyc2UgPSBiYWNrZ3JvdW5kX2dlbmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtleVR5cGUgPSAiU1lNQk9MIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPcmdEYiA9IG9yZy5Icy5lZy5kYiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbnQgPSAiQlAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBBZGp1c3RNZXRob2QgPSAiQkgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiA9IDAuMDAwMDEpCgp1bmZpbHRlcmVkX2dvX29yYV9yZXN1bHRzIDwtIGVucmljaEdPKGdlbmUgPSB1bmZpbHRlcmVkX291dGxpZXJfZ2VuZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdW5pdmVyc2UgPSBiYWNrZ3JvdW5kX2dlbmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtleVR5cGUgPSAiU1lNQk9MIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPcmdEYiA9IG9yZy5Icy5lZy5kYiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbnQgPSAiQlAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBBZGp1c3RNZXRob2QgPSAiQkgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiA9IDAuMDAwMDEpCmBgYAoKYGBge3J9CmtuZWVfZ29fcmVzdWx0cyA8LSBrbmVlX2dvX29yYV9yZXN1bHRzQHJlc3VsdCAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgZHBseXI6OmZpbHRlcihwLmFkanVzdCA8IDAuMikKa25lZV9nb19yZXN1bHRzCmBgYAoKYGBge3J9CnVuZmlsdGVyZWRfZ29fcmVzdWx0cyA8LSB1bmZpbHRlcmVkX2dvX29yYV9yZXN1bHRzQHJlc3VsdCAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgZHBseXI6OmZpbHRlcihwLmFkanVzdCA8IDAuMikKdW5maWx0ZXJlZF9nb19yZXN1bHRzCmBgYApJbiBvcmRlciB0byBzZWUgYW55IGVucmljaG1lbnQgcmVzdWx0cywgdGhlIGFkanVzdGVkIHAtdmFsdWUgdGhyZXNob2xkIGhhcyB0byBiZSBpbmNyZWFzZWQgdG8gMC4yIHNvIHRoZXJlIGlzIHZlcnkgbGl0dGxlIGNvbmZpZGVuY2UgdGhhdCB0aGVzZSBwYXRod2F5cyBhcmUgYWN0dWFsbHkgZW5yaWNoZWQuIApUaGVyZWZvcmUsIGl0IGFwcGVhcnMgdGhhdCB0aGVyZSBhcmUgbm8gc2lnbmlmaWNhbnQgcGF0aHdheXMgdGhhdCBhcmUgYWZmZWN0ZWQgYnkgdGhlIGdyb3VwIG9mIGdlbmVzIHRoYXQgYXJlIGZvdW5kIHRvIGJlIG9mZiB0aGUgZGlhZ29uYWwgYmV0d2VlbiBzcGFjZXJhbmdlciBhbmQgYWxldmluLWZyeS4gCgpMZXQncyB0YWtlIGEgbG9vayBhdCBhbGwgZ2VuZXMgdGhhdCBoYXZlIGF0IGxlYXN0IDAuNSBsb2cgZm9sZCBjaGFuZ2UgYmV0d2VlbiBTcGFjZXJhbmdlciBhbmQgQWxldmluLWZyeS4gCgpgYGB7cn0KIyBmaW5kIGdlbmVzIHRoYXQgaGF2ZSBkaWZmZXJlbnQgZ2VuZSBleHByZXNzaW9uIGluIGJvdGggc2FtcGxlcyBiZXR3ZWVuIHNwYWNlcmFuZ2VyIGFuZCBmcnkta25lZSBvciBmcnktdW5maWx0ZXJlZCAKZGlmZmVyZW50X2dlbmVfY291bnRzIDwtIHJvd2RhdGFfY29yICU+JQogIGRwbHlyOjpmaWx0ZXIoYWJzKGtuZWVfbG9nX2ZvbGRfY2hhbmdlKSA+IDAuNSB8IGFicyh1bmZpbHRlcmVkX2xvZ19mb2xkX2NoYW5nZSkgPiAwLjUpICU+JQogIGRwbHlyOjpjb3VudChnZW5lX2lkKSAlPiUgCiAgZHBseXI6OmZpbHRlcihuID09IDIpICU+JQogIGRwbHlyOjpwdWxsKGdlbmVfaWQpCmBgYAoKYGBge3J9CiMgcHJpbnQgb3V0IGxpc3Qgb2YgZ2VuZXMgd2l0aCB3aXRoID4gMS41IGdlbmUgZXhwcmVzc2lvbiBpbiBBbGV2aW4tZnJ5IAojIG9ubHkgaW5jbHVkZSBnZW5lcyB0aGF0IGFyZSBmb3VuZCBpbiBib3RoIHNhbXBsZXMgCnJvd2RhdGFfY29yICU+JQogIGRwbHlyOjpmaWx0ZXIoZ2VuZV9pZCAlaW4lIGRpZmZlcmVudF9nZW5lX2NvdW50cykgJT4lCiAgZHBseXI6OmFycmFuZ2Uoc3ltYm9sKSAlPiUKICBkcGx5cjo6c2VsZWN0KHNhbXBsZV9pZCwgc3ltYm9sLCBnZW5lX2lkLCBrbmVlX2xvZ19mb2xkX2NoYW5nZSwgCiAgICAgICAgICAgICAgICB1bmZpbHRlcmVkX2xvZ19mb2xkX2NoYW5nZSwgYGFsZXZpbi1mcnkta25lZWAsIAogICAgICAgICAgICAgICAgYGFsZXZpbi1mcnktdW5maWx0ZXJlZGAsIHNwYWNlcmFuZ2VyKQpgYGAKCgpgYGB7cn0KIyBleHRyYWN0IHRhcmdldCBnZW5lIGxpc3QgZm9yIE9SQSwgODg2IGdlbmVzCmRpZmZlcmVudF9nZW5lcyA8LSByb3dkYXRhX2NvciAlPiUKICAjIGZpbHRlciBmb3IgYW55dGhpbmcgd2l0aCBmb2xkIGNoYW5nZSA+IDEuNSBhbmQgZm91bmQgaW4gYm90aCBzYW1wbGVzCiAgZHBseXI6OmZpbHRlcihnZW5lX2lkICVpbiUgZGlmZmVyZW50X2dlbmVfY291bnRzKSAlPiUKICBkcGx5cjo6cHVsbChzeW1ib2wpICU+JQogIHVuaXF1ZSgpCmBgYAoKV2UgYXJlIG9ubHkgZ29pbmcgdG8gdXNlIG9uZSBsaXN0IG9mIGdlbmVzIGhlcmUgdG8gZG8gb3ZlciByZXByZXNlbnRhdGlvbiBhbmFseXNpcywgYmVjYXVzZSB0aGUgc2FtZSBnZW5lcyBhcmUgZm91bmQgdG8gaGF2ZSBkaWZmZXJlbnQgZ2VuZSBleHByZXNzaW9uIGJldHdlZW4gQWxldmluLWZyeSBhbmQgc3BhY2VyYW5nZXIgcmVnYXJkbGVzcyBvZiBmaWx0ZXJpbmcgc3RyYXRlZ3kgZm9yIEFsZXZpbi1mcnkuIAoKYGBge3J9CiMgcGVyZm9ybSBnZW5lIG9udG9sb2d5IGxvb2tpbmcgYXQgYWxsIGdlbmVzIHRoYXQgYXJlIGRpZmZlcmVudCAKZ29fb3JhX3Jlc3VsdHMgPC0gZW5yaWNoR08oZ2VuZSA9IGRpZmZlcmVudF9nZW5lcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgdW5pdmVyc2UgPSBiYWNrZ3JvdW5kX2dlbmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICBrZXlUeXBlID0gIlNZTUJPTCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIE9yZ0RiID0gb3JnLkhzLmVnLmRiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBvbnQgPSAiQlAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwQWRqdXN0TWV0aG9kID0gIkJIIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmID0gMC4wMDAwMSkKYGBgCgoKYGBge3J9CiMgbG9vayBhdCBnZW5lIG9udG9sb2d5IHJlc3VsdHMgCmdvX3Jlc3VsdHMgPC0gZ29fb3JhX3Jlc3VsdHNAcmVzdWx0ICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBkcGx5cjo6ZmlsdGVyKHAuYWRqdXN0IDwgMC4yKQpnb19yZXN1bHRzCmBgYAoKSXQgbG9va3MgbGlrZSB3aGVuIHlvdSBmaWx0ZXIgdG8gb25seSBpbmNsdWRlIGdlbmVzIHRoYXQgYXJlIGZvdW5kIHRvIGhhdmUgZGlmZmVyZW50IG1lYW4gZ2VuZSBleHByZXNzaW9uIGJldHdlZW4gU3BhY2VyYW5nZXIgYW5kIEFsZXZpbi1mcnkgaW4gYm90aCBzYW1wbGVzIHRoZXJlIGFyZSBubyBzcGVjaWZpYyBwYXRod2F5cyBpZGVudGlmaWVkIHRoYXQgdGhlIGdlbmVzIGJlbG9uZyB0by4gCgojIyMgU2hhcmVkIGdlbmVzIGFjcm9zcyB0b29scwoKVGhlIGZpbmFsIHRoaW5nIHdlIHdpbGwgbG9vayBhdCBpcyB0aGUgb3ZlcmxhcCBvZiBnZW5lcyBkZXRlY3RlZCBpbiBlYWNoIHRvb2wuIApGaXJzdCB3ZSBuZWVkIHRvIGZpbHRlciB0aGUgYFNwYXRpYWxFeHBlcmltZW50YCBvYmplY3RzIHRvIG9ubHkgaGF2ZSBzcG90cyB0aGF0IGFyZSBmb3VuZCBpbiBib3RoIHRvb2xzIGFuZCB0aGVuIHdlIGZpbHRlciB0byBpbmNsdWRlIG9ubHkgZ2VuZXMgdGhhdCBhcmUgZm91bmQgaW4gYm90aCBpbmRpY2VzIGJlZm9yZSBsb29raW5nIGF0IHRoZSBvdmVybGFwLiAKCmBgYHtyfQojIG1ha2UgYSBmdW5jdGlvbiB0byBmaWx0ZXIgc2NlcyBieSBzdWJzZXQgb2Ygc3BvdHMgYW5kIHJlLWNhbGN1bGF0ZSBmZWF0dXJlIHN0YXRzCmZpbHRlcl9zcGUgPC0gZnVuY3Rpb24oc3BlLCBzcG90cyl7CiAgIyByZW1vdmUgIi0xIiBhdCBlbmQgb2YgYmFyY29kZSBmb3Igc3BhY2VyYW5nZXIgc3BlcwogIGNvbG5hbWVzKHNwZSkgPC0gZ3N1YigiLTEiLCAiIiwgY29sbmFtZXMoc3BlKSkKICBjZWxsc190b19rZWVwIDwtIGNvbG5hbWVzKHNwZSkgJWluJSBzcG90cwogIHJvd0RhdGEoc3BlKSA8LSBOVUxMCiAgc3BlWywgY2VsbHNfdG9fa2VlcF0gJT4lCiAgICBzY3V0dGxlOjphZGRQZXJGZWF0dXJlUUNNZXRyaWNzKCkKfQpgYGAKCmBgYHtyfQojIGZpbHRlciBhbGwgc3BlcyB0byBvbmx5IGhhdmUgY29tbW9uIHNwb3RzIApzcGVfbGlzdF9jb21tb24gPC0gYWxsX3NwZV9maWx0ZXIgJT4lCiAgcHVycnI6Om1hcChmaWx0ZXJfc3BlLCBzcG90cyA9IGNvbW1vbl9zcG90cykKCiMgZ3JhYiByb3dkYXRhIGZyb20gZmlsdGVyZWQgc2NlcyAKcm93ZGF0YV9kZl9maWx0ZXJlZCA8LSBwdXJycjo6bWFwX2RmKHNwZV9saXN0X2NvbW1vbiwgc2NwY2FUb29sczo6cm93ZGF0YV90b19kZiwgLmlkID0gInRvb2wiKSAlPiUKICBkcGx5cjo6bXV0YXRlKHNhbXBsZV9pZCA9IHN0cmluZ3I6OndvcmQodG9vbCwgMSwgc2VwID0gIi0iKSwKICAgICAgICAgICAgICAgIHRvb2wgPSBkcGx5cjo6Y2FzZV93aGVuKHRvb2wgJWluJSBmcnlfa25lZV9uYW1lcyB+ICJhbGV2aW4tZnJ5LWtuZWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9vbCAlaW4lIGZyeV91bmZpbHRlcmVkX25hbWVzIH4gImFsZXZpbi1mcnktdW5maWx0ZXJlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b29sICVpbiUgZnJ5X3VuZmlsdGVyZWRfc2tldGNoX25hbWVzIH4gImFsZXZpbi1mcnktdW5maWx0ZXJlZC1za2V0Y2giLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvb2wgJWluJSBzcGFjZXJhbmdlcl9uYW1lcyB+ICJzcGFjZXJhbmdlciIpKSAlPiUKICBkcGx5cjo6bGVmdF9qb2luKHNhbXBsZV9pbmZvX2RmLAogICAgICAgICAgICAgICAgICAgYnkgPSBjKCJ0b29sIiwgInNhbXBsZV9pZCIgPSAic2FtcGxlIikpCmBgYAoKCmBgYHtyfQojIGdldCBnZW5lcyBjb21tb24gaW4gYWxsIHRvb2xzCmNvbW1vbl9nZW5lcyA8LSByb3dkYXRhX2RmX2ZpbHRlcmVkICU+JQogIGRwbHlyOjpzZWxlY3QoZ2VuZV9pZCwgdG9vbCkgJT4lCiAgZHBseXI6OmRpc3RpbmN0KCkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGdlbmVfaWQpICU+JQogIGRwbHlyOjp0YWxseSgpICU+JQogICMgZmlsdGVyIGZvciBnZW5lcyBmb3VuZCBpbiBib3RoIHNwYWNlcmFuZ2VyIGFuZCBhbGV2aW4tZnJ5CiAgZHBseXI6OmZpbHRlcihuID09IDQpICU+JQogIGRwbHlyOjpwdWxsKGdlbmVfaWQpCgojIGZpbHRlciByb3dkYXRhX2RmIHRvIG9ubHkgaW5jbHVkZSBnZW5lcyBmb3VuZCBpbiBhbGwgdG9vbHMgYW5kIGdlbmVzIHdpdGggbWVhbiA+IDAgYW5kIGRldGVjdGVkID4gMCBpbiBhbGwgY2VsbHMKcm93ZGF0YV9kZl9maWx0ZXJlZCA8LSByb3dkYXRhX2RmX2ZpbHRlcmVkICU+JSAKICBkcGx5cjo6ZmlsdGVyKGdlbmVfaWQgJWluJSBjb21tb25fZ2VuZXMpICU+JQogIGRwbHlyOjpmaWx0ZXIobWVhbiA+IDAgJiBkZXRlY3RlZCA+IDApCmBgYAoKCmBgYHtyfQojIGNyZWF0ZSB1cHNldCBwbG90IApnZW5lX2RldGVjdF9kZiA8LSByb3dkYXRhX2RmX2ZpbHRlcmVkICU+JQogIGRwbHlyOjpncm91cF9ieShnZW5lX2lkKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKHRvb2xzX2RldGVjdGVkID0gbGlzdCh1bmlxdWUodG9vbCkpKQoKZ2dwbG90KGdlbmVfZGV0ZWN0X2RmLCBhZXMoeCA9IHRvb2xzX2RldGVjdGVkKSkgKwogIGdlb21fYmFyKCkgKwogIHNjYWxlX3hfdXBzZXQobl9pbnRlcnNlY3Rpb25zID0gNSkKYGBgCgpGb3IgdGhlIG1vc3QgcGFydCBnZW5lcyBhcmUgZm91bmQgaW4gYWxsIHRvb2xzLCBob3dldmVyIGl0IGxvb2tzIGxpa2UgdGhlcmUgaXMgYSBjaHVuayBvZiBnZW5lcyB0aGF0IHdlIHdvdWxkIG1pc3MgdGhhdCBhcmUgZm91bmQgaW4gU3BhY2VyYW5nZXIgdGhhdCBhcmUgbm90IGZvdW5kIGluIGVpdGhlciBvZiB0aGUgQWxldmluLWZyeSB0b29scy4gCkl0IGFsc28gYXBwZWFycyB0aGF0IHRoZXJlIGlzIGEgZ3JvdXAgb2YgZ2VuZXMgc3BlY2lmaWMgdG8gQWxldmluLWZyeS11bmZpbHRlcmVkIGAtLXNrZXRjaGAgYW5kIGEgZ3JvdXAgb2YgZ2VuZXMgdGhhdCBhcmUgb25seSBmb3VuZCB0byBvdmVybGFwIGJldHdlZW4gU3BhY2VyYW5nZXIgYW5kIEFsZXZpbi1mcnkgd2l0aCBgLS1za2V0Y2hgLiAKQXJlIGFueSBvZiB0aGUgZ2VuZXMgdGhhdCBhcmUgc3BlY2lmaWMgdG8gU3BhY2VyYW5nZXIgZ2VuZXMgaW52b2x2ZWQgaW4gaW1wb3J0YW50IHBhdGh3YXlzIHRoYXQgd2UgbWlnaHQgd2FudCB0byBtYWtlIHN1cmUgdGhhdCB3ZSBkb24ndCBsb3NlPyAKCmBgYHtyfQojIHNwYWNlcmFuZ2VyIG9ubHkgZ2VuZXMgdG8gZG8gT1JBIApzcGFjZXJhbmdlcl9vbmx5X2dlbmVzIDwtIGdlbmVfZGV0ZWN0X2RmICU+JQogIGRwbHlyOjpmaWx0ZXIodG9vbHNfZGV0ZWN0ZWQgPT0gInNwYWNlcmFuZ2VyIikgJT4lCiAgZHBseXI6OnNlbGVjdChnZW5lX2lkKSAlPiUKICBkcGx5cjo6bGVmdF9qb2luKGdlbmVfc3ltYm9sc19kZiwgYnk9ICJnZW5lX2lkIikgJT4lCiAgdGlkeXI6OmRyb3BfbmEoKSAlPiUKICB1bmlxdWUoKSAlPiUKICBkcGx5cjo6cHVsbChzeW1ib2wpCgpzcGFjZXJhbmdlcl9vbmx5X2dlbmVzCmBgYAoKYGBge3J9CnNwYWNlcmFuZ2VyX29yYV9yZXN1bHRzIDwtIGVucmljaEdPKGdlbmUgPSBzcGFjZXJhbmdlcl9vbmx5X2dlbmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICB1bml2ZXJzZSA9IGJhY2tncm91bmRfZ2VuZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGtleVR5cGUgPSAiU1lNQk9MIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgT3JnRGIgPSBvcmcuSHMuZWcuZGIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG9udCA9ICJCUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHBBZGp1c3RNZXRob2QgPSAiQkgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgPSAwLjAwMDAxKQpgYGAKCgpgYGB7cn0KIyBsb29rIGF0IGdlbmUgb250b2xvZ3kgcmVzdWx0cyAKc3BhY2VyYW5nZXJfZ29fcmVzdWx0cyA8LSBzcGFjZXJhbmdlcl9vcmFfcmVzdWx0c0ByZXN1bHQgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIGRwbHlyOjpmaWx0ZXIocC5hZGp1c3QgPCAwLjIpCnNwYWNlcmFuZ2VyX2dvX3Jlc3VsdHMKYGBgCgpUaGUgZ2VuZXMgdGhhdCBhcmUgaWRlbnRpZmllZCBpbiBvbmx5IFNwYWNlcmFuZ2VyIGFyZSBub3QgZW5yaWNoZWQgZm9yIGFueSBzcGVjaWZpYyBwYXRod2F5IGFuZCBhbHNvIGFwcGVhciB0byBiZSBjb21wcmlzZWQgb2YgYSBsb3Qgb2Ygcmlib3NvbWFsIGdlbmVzLCBtaXRvY2hvbmRyaWFsIGdlbmVzLCBhbmQgbG9uZyBpbnRlcmdlbmljIG5vbi1jb2RpbmcgUk5BJ3MuIAoKYGBge3J9CiMgc2F2ZSBzcGUgbGlzdApzcGVfZmlsZSA8LSBmaWxlLnBhdGgocmVzdWx0c19kaXIsICJhbGxfc3BlX2xpc3RfZW5zZW1ibF92MTA0LnJkcyIpCnJlYWRyOjp3cml0ZV9yZHMoYWxsX3NwZV9saXN0LCBzcGVfZmlsZSkKYGBgCgoKIyMgQ29uY2x1ZGluZyB0aG91Z2h0cyAKCi0gQWxldmluLWZyeSBhbmQgU3BhY2VyYW5nZXIgb25seSByZXN1bHQgaW4gc2ltaWxhciBkaXN0cmlidXRpb25zIG9mIFVNSS9zcG90IGFuZCBnZW5lcy9zcG90LCBhbHRob3VnaCBpdCBpcyBub3QgcXVpdGUgYXMgbmljZSBhcyB0aGUgb3ZlcmxheSB5b3Ugc2VlIHdpdGggc2luZ2xlLWNlbGwgbGlicmFyaWVzLiAKLSBVc2luZyBhbGV2aW4tZnJ5LXVuZmlsdGVyZWQgcmVzdWx0cyBpbiBpZGVudGlmaWNhdGlvbiBvZiB0aGUgc2FtZSBzcG90cyB0aGF0IGFyZSBpZGVudGlmaWVkIGluIFNwYWNlcmFuZ2VyLiAKLSBJdCBhcHBlYXJzIHRoYXQgU3BhY2VyYW5nZXIgaGFzIHNsaWdodGx5IGhpZ2hlciBVTUkvY2VsbCBhbmQgZ2VuZXMgZGV0ZWN0ZWQvY2VsbC4gCi0gVGhlIGluY3JlYXNlIGluIFVNSS9jZWxsIGFuZCBnZW5lcyBkZXRlY3RlZC9jZWxsIGluIFNwYWNlcmFuZ2VyIGRpc2FwcGVhcnMgd2l0aCB1c2Ugb2YgYC0tc2tldGNoYCBhbGlnbm1lbnQgd2l0aCBBbGV2aW4tZnJ5LiAKLSBCb3RoIHRvb2xzIGFsc28gc2hvdyBoaWdoIGNvcnJlbGF0aW9uIGluIG1lYW4gZ2VuZSBleHByZXNzaW9uIGFuZCBoaWdoIG92ZXJsYXAgaW4gdGhlIGdlbmVzIHRoYXQgYXJlIGRldGVjdGVkLCBhbHRob3VnaCB0aGVyZSBpcyBhbiBpbmNyZWFzZSBpbiBnZW5lIGV4cHJlc3Npb24gaW4gU3BhY2VyYW5nZXIgYWxvbmUuIAotIFRoZSBpbmNyZWFzZSBpbiBnZW5lIGV4cHJlc3Npb24gZm91bmQgaW4gU3BhY2VyYW5nZXIgYWxzbyBkZWNyZWFzZXMgdGhyb3VnaCB1c2Ugb2YgYC0tc2tldGNoYCBhbGlnbm1lbnQgYW5kIG1vcmUgZ2VuZXMgdGhhdCB3ZXJlIHF1YW50aWZpZWQgaW4gU3BhY2VyYW5nZXIgYW5kIHdlcmUgbm90IHF1YW50aWZpZWQgaW4gQWxldmluLWZyeSB3aXRoIHNlbGVjdGl2ZSBhbGlnbm1lbnQgYXJlIG5vdyBxdWFudGlmaWVkIHdpdGggYC0tc2tldGNoYC4gCgojIyBTZXNzaW9uIEluZm8KCmBgYHtyfQpzZXNzaW9uaW5mbzo6c2Vzc2lvbl9pbmZvKCkKYGBgCgo=
=======
LS0tCnRpdGxlOiAiU3BhdGlhbCBUcmFuc2NyaXB0b21pY3MgQmVuY2htYXJraW5nIgphdXRob3I6ICJBbGx5IEhhd2tpbnMgZm9yIENDREwiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKSW4gdGhpcyBub3RlYm9vayB3ZSBhcmUgY29tcGFyaW5nIHRoZSB1c2Ugb2YgQWxldmluLWZyeSBhbmQgU3BhY2VyYW5nZXIgZm9yIHF1YW50aWZ5aW5nIHNwYXRpYWwgdHJhbnNjcmlwdG9taWNzIGxpYnJhcmllcy4gClR3byBzcGF0aWFsIHRyYW5zY3JpcHRvbWljIGxpYnJhcmllcyB3ZXJlIHF1YW50aWZpZWQgdXNpbmcgQWxldmluLWZyeSBhbmQgU3BhY2VyYW5nZXIgYW5kIHRoZSByZXN1bHRzIHdlcmUgY29tYmluZWQgZm9sbG93aW5nIHRoZSBbQWxldmluLWZyeSB0dXRvcmlhbF0oaHR0cHM6Ly9jb21iaW5lLWxhYi5naXRodWIuaW8vYWxldmluLWZyeS10dXRvcmlhbHMvMjAyMS9hZi1zcGF0aWFsLykuIApXZSBhcmUgY29tcGFyaW5nIHRoYXQgdG8gaWYgd2Ugd2VyZSB0byBub3QgaW50ZWdyYXRlIHRoZSBTcGFjZXJhbmdlciBkYXRhIHdpdGggQWxldmluLWZyeSBhbmQgb25seSB1c2UgU3BhY2VyYW5nZXIuIApXaGVuIHBlcmZvcm1pbmcgdGhpcyBhbmFseXNpcyBhbGwgdG9vbHMgdXNlZCBhbiBpbmRleCB3aXRoIEVuc2VtYmwgMTA0LgpIZXJlIHdlIHdpbGwgbG9vayBhdCB0d28gbGlicmFyaWVzLCBTQ1BDUjAwMDM3MiBhbmQgU0NQQ1IwMDAzNzMuCkFsZXZpbi1mcnkgd2FzIHJ1biBib3RoIHVzaW5nIHRoZSBrbmVlIGZpbHRlcmluZyBhbmQgdGhlIHVuZmlsdGVyZWQgcGVybWl0IGxpc3QgbW9kZS4gCgpOb3RlIHRoYXQgYFNwYXRpYWxFeHBlcmltZW50YCB3YXMgaW5zdGFsbGVkIGZyb20gR2l0aHViLCBpbiBvcmRlciB0byByZWZsZWN0IHRoZSBtb3N0IHJlY2VudCBjaGFuZ2VzIGluIGByZWFkMTBYVmlzaXVtYCBhdCBjb21taXQgZGRiMTVlMC4KCiMjIFNldCBVcAoKYGBge3J9CmxpYnJhcnkobWFncml0dHIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKbGlicmFyeShTcGF0aWFsRXhwZXJpbWVudCkKbGlicmFyeShnZ3Vwc2V0KQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKbGlicmFyeShvcmcuSHMuZWcuZGIpCgojIHNldCBzZWVkIGZvciBPUkEKc2V0LnNlZWQoMjAyMSkKYGBgCgpgYGB7cn0KIyBsb2FkIGluIGJlbmNobWFya2luZyBmdW5jdGlvbnMgdGhhdCB3aWxsIGJlIHVzZWQgZm9yIGNvcHlpbmcgZGF0YSBhbmQgZ2VuZXJhdGluZyBzYW1wbGUgdGFibGVzCmZ1bmN0aW9uX3BhdGggPC0gZmlsZS5wYXRoKCIuLiIgLCJiZW5jaG1hcmtpbmctZnVuY3Rpb25zIiwgIlIiKQpmaWxlLnBhdGgoZnVuY3Rpb25fcGF0aCwgbGlzdC5maWxlcyhmdW5jdGlvbl9wYXRoLCBwYXR0ZXJuID0gIiouUiQiKSkgJT4lCiAgcHVycnI6OndhbGsoc291cmNlKQpgYGAKCgpgYGB7cn0KIyBzZXQgdXAgZmlsZSBwYXRocyAKYmFzZV9kaXIgPC0gaGVyZTo6aGVyZSgpCgojIGZvbGRlciB3aXRoIGFsZXZpbi1mcnkgYW5kIGNlbGxyYW5nZXIgcXVhbnRzIGZyb20gUzMKZGF0YV9kaXIgPC0gZmlsZS5wYXRoKGJhc2VfZGlyLCAiZGF0YSIsICJzcGF0aWFsIikgCnF1YW50c19kaXIgPC0gZmlsZS5wYXRoKGRhdGFfZGlyLCAiZGF0YSIsICJxdWFudHMiKQoKIyByZXN1bHRzIGRpcmVjdG9yeSAKcmVzdWx0c19kaXIgPC0gZmlsZS5wYXRoKGRhdGFfZGlyLCAicmVzdWx0cyIpCgojIHNhbXBsZSBuYW1lCnNhbXBsZV9pZHMgPC0gYygiU0NQQ1IwMDAzNzIiLCAiU0NQQ1IwMDAzNzMiKQpgYGAKCgpgYGB7cn0KbWl0b19maWxlIDwtIGZpbGUucGF0aChiYXNlX2RpciwgInNhbXBsZS1pbmZvIiwgIkhvbW9fc2FwaWVucy5HUkNoMzguMTA0Lm1pdG9nZW5lcy50eHQiKQogIAojIHJlYWQgaW4gbWl0byBnZW5lcyAKbWl0b19nZW5lcyA8LSByZWFkcjo6cmVhZF90c3YobWl0b19maWxlLCBjb2xfbmFtZXMgPSAiZ2VuZV9pZCIpCm1pdG9fZ2VuZXMgPC0gbWl0b19nZW5lcyAlPiUKICBkcGx5cjo6cHVsbChnZW5lX2lkKSAlPiUKICB1bmlxdWUoKQpgYGAKCiMjIENvcHkgZnJvbSBTMwoKYGBge3IgZXZhbD1GQUxTRX0KIyBkb3dubG9hZCBhbGV2aW4gZnJ5IGFuZCBjZWxscmFuZ2VyIG91dHB1dAphd3NfY29weV9zYW1wbGVzKGxvY2FsX2RpciA9IHF1YW50c19kaXIsCiAgICAgICAgICAgICAgICAgczNfZGlyID0gInMzOi8vbmV4dGZsb3ctY2NkbC1yZXN1bHRzL3NjcGNhIiwKICAgICAgICAgICAgICAgICBzYW1wbGVzID0gc2FtcGxlX2lkcywKICAgICAgICAgICAgICAgICB0b29scyA9IGMoImFsZXZpbi1mcnkta25lZSIsICJhbGV2aW4tZnJ5LXVuZmlsdGVyZWQiLCAiY2VsbHJhbmdlciIpKQpgYGAKCgojIyBBbGV2aW4tZnJ5ICsgU3BhY2VyYW5nZXIgdmVyc3VzIFNwYWNlcmFuZ2VyIE9ubHkKCk5vdyBsZXQncyB0YWtlIGEgbG9vayBhdCBjb21wYXJpbmcgdGhlIHR3byBtZXRob2RzIG9mIHVzaW5nIEFsZXZpbi1mcnkgKyBTcGFjZXJhbmdlciB0byBvbmx5IFNwYWNlcmFuZ2VyIGZvciBxdWFudGlmaWNhdGlvbi4gClRvIGRvIHRoaXMsIHdlIHdpbGwgcmVhZCBpbiB0aGUgQWxldmluLWZyeSArIFNwYWNlcmFuZ2VyIGNvbWJpbmVkIGFuZCBTcGFjZXJhbmdlciBvbmx5IGBTcGF0aWFsRXhwZXJpbWVudGAgb2JqZWN0cyBzZXBhcmF0ZWx5IGFuZCB0aGVuIG1lcmdlIHRoZW0gaW50byBvbmUgbGlzdCBiZWZvcmUgZ3JhYmJpbmcgdGhlIHBlciBjZWxsIGFuZCBwZXIgZ2VuZSBxdWFsaXR5IG1ldHJpY3MuIAoKIyMjIENyZWF0ZSBTcGF0aWFsIEV4cGVyaW1lbnRzIAoKYGBge3J9CiMgZ2V0IHBhdGggdG8gZnJ5IGtuZWUgb3V0cHV0IGRpcmVjdG9yeSAKZnJ5X2tuZWVfZGlyIDwtIGZpbGUucGF0aChxdWFudHNfZGlyLCAiYWxldmluLWZyeS1rbmVlIiwgc2FtcGxlX2lkcykKZnJ5X2tuZWVfZGlyIDwtIHBhc3RlMChmcnlfa25lZV9kaXIsICItSG9tb19zYXBpZW5zLkdSQ2gzOC4xMDQuc3BsaWNlZF9pbnRyb24udHhvbWUtc2FsaWduLWNyLWxpa2UtZW0ta25lZSIpCgojIGdldCBwYXRoIHRvIGZyeSB1bmZpbHRlcmVkIG91dHB1dCBkaXJlY3RvcnkgCmZyeV91bmZpbHRlcmVkX2RpciA8LSBmaWxlLnBhdGgocXVhbnRzX2RpciwgImFsZXZpbi1mcnktdW5maWx0ZXJlZCIsIHNhbXBsZV9pZHMpCmZyeV91bmZpbHRlcmVkX2RpciA8LSBwYXN0ZTAoZnJ5X3VuZmlsdGVyZWRfZGlyLCAiLUhvbW9fc2FwaWVucy5HUkNoMzguMTA0LnNwbGljZWRfaW50cm9uLnR4b21lLXNhbGlnbi1jci1saWtlLWVtIikKCiMgcGF0aHMgdG8gc3BhdGlhbCBmb2xkZXJzIApjZWxscmFuZ2VyX2ZvbGRlcnMgPC0gcGFzdGUwKHNhbXBsZV9pZHMsICItR1JDaDM4XzEwNF9jZWxscmFuZ2VyX2Z1bGwtc3BhdGlhbCIpCnNwYWNlcmFuZ2VyX2RpciA8LSBmaWxlLnBhdGgocXVhbnRzX2RpciwgImNlbGxyYW5nZXIiLCBjZWxscmFuZ2VyX2ZvbGRlcnMpCmBgYAoKCmBgYHtyfQojIHJlYWQgaW4gY29tYmluZWQgZnJ5IGFuZCBzcGFjZXJhbmdlciBzcGUgZm9yIGZyeSBrbmVlCmZyeV9rbmVlX3NwZV8xIDwtIGNyZWF0ZV9mcnlfc3BhY2VyYW5nZXJfc3BlKGZyeV9rbmVlX2RpclsxXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwYWNlcmFuZ2VyX2RpclsxXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9pZHNbMV0pCgpmcnlfa25lZV9zcGVfMiA8LSBjcmVhdGVfZnJ5X3NwYWNlcmFuZ2VyX3NwZShmcnlfa25lZV9kaXJbMl0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGFjZXJhbmdlcl9kaXJbMl0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfaWRzWzJdKQpgYGAKCmBgYHtyfQojIHJlYWQgaW4gY29tYmluZWQgZnJ5IGFuZCBzcGFjZXJhbmdlciBzcGUgZm9yIGZyeSB1bmZpbHRlcmVkCmZyeV91bmZpbHRlcmVkX3NwZV8xIDwtIGNyZWF0ZV9mcnlfc3BhY2VyYW5nZXJfc3BlKGZyeV91bmZpbHRlcmVkX2RpclsxXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwYWNlcmFuZ2VyX2RpclsxXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9pZHNbMV0pCgpmcnlfdW5maWx0ZXJlZF9zcGVfMiA8LSBjcmVhdGVfZnJ5X3NwYWNlcmFuZ2VyX3NwZShmcnlfdW5maWx0ZXJlZF9kaXJbMl0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGFjZXJhbmdlcl9kaXJbMl0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfaWRzWzJdKQpgYGAKCgoKYGBge3J9CiMgc3BhY2VyYW5nZXIgb3V0cHV0IHBhdGhzCnNwYWNlcmFuZ2VyX2RpciA8LSBmaWxlLnBhdGgocXVhbnRzX2RpciwgImNlbGxyYW5nZXIiLCBjZWxscmFuZ2VyX2ZvbGRlcnMpCgojIHJlYWQgaW4gc3BhY2VyYW5nZXIgb3V0cHV0IGRpcmVjdGx5IHVzaW5nIHJlYWQxMFhWaXNpdW0Kc3BhY2VyYW5nZXJfc3BlXzEgPC0gcmVhZDEweFZpc2l1bShmaWxlLnBhdGgoc3BhY2VyYW5nZXJfZGlyWzFdLCAib3V0cyIpLCBzYW1wbGVfaWQgPSBzYW1wbGVfaWRzWzFdKQpzcGFjZXJhbmdlcl9zcGVfMiA8LSByZWFkMTB4VmlzaXVtKGZpbGUucGF0aChzcGFjZXJhbmdlcl9kaXJbMl0sICJvdXRzIiksIHNhbXBsZV9pZCA9IHNhbXBsZV9pZHNbMl0pCmBgYAoKIyMjIFBlciBTcG90IFFDIE1ldHJpY3MKCk5vdyB0aGF0IHdlIGhhdmUgcmVhZCBpbiB0aGUgZGF0YSBhbmQgY3JlYXRlZCBvdXIgdHdvIGBTcGF0aWFsRXhwZXJpbWVudGAgb2JqZWN0cywgd2UgY2FuIGdvIGFoZWFkIGFuZCBjb21iaW5lIHRoZW0gaW50byBvbmUgbGlzdCBhbmQgdGhlbiBjYWxjdWxhdGUgdGhlIHBlciBzcG90IFFDIG1ldHJpY3MgdXNpbmcgYHNjdXR0bGU6OmFkZFBlckNlbGxRQ01ldHJpY3MoKS5gCgpgYGB7cn0KIyBjcmVhdGUgb25lIGxpc3Qgd2l0aCBib3RoIHNwZSdzIHRvZ2V0aGVyCmFsbF9zcGVfbGlzdCA8LSBsaXN0KGZyeV9rbmVlX3NwZV8xLCBmcnlfdW5maWx0ZXJlZF9zcGVfMSwgc3BhY2VyYW5nZXJfc3BlXzEsIGZyeV9rbmVlX3NwZV8yLCBmcnlfdW5maWx0ZXJlZF9zcGVfMiwgc3BhY2VyYW5nZXJfc3BlXzIpCgojIG5hbWUgZWFjaCBzcGUgd2l0aCBjb21iaW5hdGlvbiBvZiBzYW1wbGVfaWQtdG9vbCAKc3BlX25hbWVzIDwtIGMoIlNDUENSMDAwMzcyLWFsZXZpbi1mcnkta25lZSIsICJTQ1BDUjAwMDM3Mi1hbGV2aW4tZnJ5LXVuZmlsdGVyZWQiLCAiU0NQQ1IwMDAzNzItc3BhY2VyYW5nZXIiLAogICAgICAgICAgICAgICAiU0NQQ1IwMDAzNzMtYWxldmluLWZyeS1rbmVlIiwgIlNDUENSMDAwMzczLWFsZXZpbi1mcnktdW5maWx0ZXJlZCIsICJTQ1BDUjAwMDM3My1zcGFjZXJhbmdlciIpCm5hbWVzKGFsbF9zcGVfbGlzdCkgPC0gc3BlX25hbWVzCgojIGNhbGN1bGF0ZSBwZXIgY2VsbCBRQyBhbmQgb3V0cHV0IHRvIGEgY29tYmluZWQgZGF0YSBmcmFtZSB3aXRoIHBsb3R0aW5nIAphbGxfc3BlX2xpc3QgPC0gYWxsX3NwZV9saXN0ICU+JQogICAgcHVycnI6Om1hcCgKICAgICAgfiBzY3V0dGxlOjphZGRQZXJDZWxsUUNNZXRyaWNzKC54LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnNldHMgPSBsaXN0KG1pdG8gPSBtaXRvX2dlbmVzW21pdG9fZ2VuZXMgJWluJSByb3duYW1lcygueCldKSkpCmBgYAoKQWZ0ZXIgYWRkaW5nIGluIHRoZSBwZXIgc3BvdCBRQyBtZXRyaWNzIHRvIGJvdGggb2YgdGhlIHNwZSdzLCB3ZSB3YW50IHRvIGV4dHJhY3QgdGhlIGBjb2xEYXRhYCBmcm9tIGVhY2ggc3BlIGFuZCBjcmVhdGUgYSBkYXRhIGZyYW1lIHRoYXQgd2UgY2FuIHVzZSBmb3IgcGxvdHRpbmcuIApXZSB3aWxsIGFsc28gbmVlZCBzb21lIGluZm9ybWF0aW9uIGFib3V0IGVhY2ggc2FtcGxlIGFuZCBob3cgaXQgd2FzIHJ1biwgc28gd2Ugd2lsbCBjcmVhdGUgYSBzYW1wbGUgbWV0YWRhdGEgdGFibGUsIGBzYW1wbGVfaW5mb19kZmAgdGhhdCB3aWxsIHRoZW4gYmUgbWVyZ2VkIHdpdGggdGhlIGBjb2xEYXRhYC4gCgpgYGB7cn0KIyBjcmVhdGUgc2FtcGxlIGluZm8gZGF0YWZyYW1lIHRvIGJlIGpvaW5lZCB3aXRoIHBlciBzcG90IGRhdGFmcmFtZSBsYXRlcgpzYW1wbGVfaW5mb19kZiA8LSBxdWFudF9pbmZvX3RhYmxlKGRhdGFfZGlyPSBxdWFudHNfZGlyLCAKICAgICAgICAgICAgICAgICB0b29scyA9IGMoImNlbGxyYW5nZXIiLCAiYWxldmluLWZyeS1rbmVlIiwgImFsZXZpbi1mcnktdW5maWx0ZXJlZCIpLAogICAgICAgICAgICAgICAgIHNhbXBsZXMgPSBzYW1wbGVfaWRzKSAlPiUKICAjIGNvbnZlcnQgY2VsbHJhbmdlciB0byBzcGFjZXJhbmdlciBhbmQgcGFzdGUgZmlsdGVyaW5nIHN0cmF0ZWd5IHRvIGFsZXZpbi1mcnkKICBkcGx5cjo6bXV0YXRlKHRvb2wgPSBpZmVsc2UodG9vbCA9PSAiY2VsbHJhbmdlciIsICJzcGFjZXJhbmdlciIsIHBhc3RlKHRvb2wsIGZpbHRlcl9zdHJhdGVneSwgc2VwID0gIi0iKSkpCgpzYW1wbGVfaW5mb19kZgpgYGAKCldoZW4gd2UgY29udmVydCB0aGUgYGNvbERhdGFgIHRvIGEgZGF0YSBmcmFtZSB3ZSB1c2UgdGhlIGN1c3RvbSBmdW5jdGlvbiwgYHNwYXRpYWxfY29sZGF0YV90b19kZigpYCB0byBkbyBzbyBhbmQgYXBwbHkgaXQgdG8gZWFjaCBzcGUgaW4gb3VyIGxpc3QuIApgYGB7cn0KZnJ5X2tuZWVfbmFtZXMgPC0gYygiU0NQQ1IwMDAzNzItYWxldmluLWZyeS1rbmVlIiwgIlNDUENSMDAwMzczLWFsZXZpbi1mcnkta25lZSIpCmZyeV91bmZpbHRlcmVkX25hbWVzIDwtIGMoIlNDUENSMDAwMzcyLWFsZXZpbi1mcnktdW5maWx0ZXJlZCIsICJTQ1BDUjAwMDM3My1hbGV2aW4tZnJ5LXVuZmlsdGVyZWQiKQpzcGFjZXJhbmdlcl9uYW1lcyA8LSBjKCJTQ1BDUjAwMDM3Mi1zcGFjZXJhbmdlciIsICJTQ1BDUjAwMDM3My1zcGFjZXJhbmdlciIpCgojIGpvaW4gY29sZGF0YSBkYXRhZnJhbWUgd2l0aCBzYW1wbGUgaW5mbwpjb2xkYXRhX2RmIDwtIGFsbF9zcGVfbGlzdCAlPiUKICBwdXJycjo6bWFwX2RmKHNwYXRpYWxfY29sZGF0YV90b19kZiwgLmlkID0gInRvb2wiKSAlPiUKICAjIHJlbW92ZSBleHRyYSAtMSBmcm9tIHNwYWNlcmFuZ2VyIGJhcmNvZGVzCiAgZHBseXI6Om11dGF0ZShzcG90X2lkID0gZ3N1YigiLTEiLCAiIiwgc3BvdF9pZCksCiAgICAgICAgICAgICAgICAjIHJlbW92ZSB0b29sIGZyb20gc2FtcGxlIGlkIAogICAgICAgICAgICAgICAgc2FtcGxlX2lkID0gc3RyaW5ncjo6d29yZChzYW1wbGVfaWQsIDEsIHNlcCA9ICItIiksCiAgICAgICAgICAgICAgICAjIHJlbW92ZSBzYW1wbGUgaWQgZnJvbSB0b29sIAogICAgICAgICAgICAgICAgdG9vbCA9IGRwbHlyOjpjYXNlX3doZW4odG9vbCAlaW4lIGZyeV9rbmVlX25hbWVzIH4gImFsZXZpbi1mcnkta25lZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b29sICVpbiUgZnJ5X3VuZmlsdGVyZWRfbmFtZXMgfiAiYWxldmluLWZyeS11bmZpbHRlcmVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvb2wgJWluJSBzcGFjZXJhbmdlcl9uYW1lcyB+ICJzcGFjZXJhbmdlciIpKSAlPiUKICBkcGx5cjo6bGVmdF9qb2luKHNhbXBsZV9pbmZvX2RmLAogICAgICAgICAgICAgICAgICAgYnkgPSBjKCJ0b29sIiwgInNhbXBsZV9pZCIgPSAic2FtcGxlIikpICU+JQogICMgcmVtb3ZlIHNwb3RzIHRoYXQgYXJlIG5vdCBvdmVybGFwcGluZyB0aXNzdWUgCiAgZHBseXI6OmZpbHRlcihpbl90aXNzdWUgPT0gMSkKYGBgCgpOb3cgd2Ugb25seSB3YW50IHRvIGZpbHRlciBvdXIgZGF0YSBmcmFtZSB0byBjb250YWluIHNwb3RzIHRoYXQgYXJlIHNoYXJlZCBiZXR3ZWVuIGJvdGggdG9vbHMuIAoKYGBge3J9CiMgaWRlbnRpZnkgc2hhcmVkIHNwb3RzIG9ubHkgCnNwb3RfY291bnRzIDwtIGNvbGRhdGFfZGYgJT4lCiAgZHBseXI6OmNvdW50KHNwb3RfaWQsIHNhbXBsZV9pZCkKYGBgCgpgYGB7cn0KIyBob3cgbWFueSBzcG90cyBhcmUgc2hhcmVkIGFtb25nIHRoZSB0b29scwpzcG90X2NvdW50c19wbG90IDwtIGNvbGRhdGFfZGYgJT4lCiAgZHBseXI6Omdyb3VwX2J5KHNwb3RfaWQsIHNhbXBsZV9pZCkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZSh0b29sc19kZXRlY3RlZCA9IGxpc3QodW5pcXVlKHRvb2wpKSkKCmdncGxvdChzcG90X2NvdW50c19wbG90LCBhZXMoeCA9IHRvb2xzX2RldGVjdGVkKSkrCiAgZ2VvbV9iYXIoKSArCiAgc2NhbGVfeF91cHNldChuX2ludGVyc2VjdGlvbnMgPSA0KQpgYGAKRm9yIHRoZSBtb3N0IHBhcnQsIHRoZSBtYWpvcml0eSBvZiB0aGUgc3BvdHMgaWRlbnRpZmllZCBhcmUgZm91bmQgaW4gYm90aCBTcGFjZXJhbmdlciBhbG9uZSBhbmQgdGhlIGNvbWJpbmF0aW9uIHdpdGggQWxldmluLWZyeS1rbmVlIGFuZCBBbGV2aW4tZnJ5LXVuZmlsdGVyZWQsIHdpdGggYSBzbWFsbCBzdWJzZXQgYmVpbmcgaWRlbnRpZmllZCBpbiBTcGFjZXJhbmdlciBhbmQgQWxldmluLWZyeS11bmZpbHRlcmVkLgpJdCBhcHBlYXJzIHRoYXQgdXNpbmcgYWxldmluLWZyeS11bmZpbHRlcmVkIGRvZXMgZ2l2ZSB1cyBzb21lIHNwb3RzIHRoYXQgdXNpbmcgdGhlIGtuZWUgbWV0aG9kIGRvZXMgbm90IGdpdmUgdXMgYW5kIHdlIGRvbid0IHNlZSBhbnkgbG9zcyBvZiBzcG90cy4gCgpMZXQncyBmaWx0ZXIgdG8gb25seSBpbmNsdWRlIHRoZXNlIGNvbW1vbiBzcG90cy4KCmBgYHtyfQpjb21tb25fc3BvdHMgPC0gc3BvdF9jb3VudHMgJT4lCiAgZHBseXI6OmZpbHRlcihuID09IDMpICU+JQogIGRwbHlyOjpwdWxsKHNwb3RfaWQpCgpjb2xkYXRhX2RmX2NvbW1vbiA8LSBjb2xkYXRhX2RmICU+JQogIGRwbHlyOjpmaWx0ZXIoc3BvdF9pZCAlaW4lIGNvbW1vbl9zcG90cykKYGBgCgpXZSB3aWxsIGFsc28gbmVlZCB0byBmaWx0ZXIgdGhlIHNwZSdzIGRpcmVjdGx5IGJhc2VkIG9uIHNwb3RzIHRoYXQgYXJlIHByZXNlbnQgaW4gdGhlIHRpc3N1ZSwgc28gd2UgY3JlYXRlIGEgc21hbGwgZnVuY3Rpb24gdG8gZG8gdGhpcyBhbmQgdGhlbiBhcHBseSBpdCB0byBib3RoIHNwZSdzIGluIHRoZSBsaXN0LiAKCmBgYHtyfQojIHdlIHdpbGwgYWxzbyB3YW50IHRvIGZpbHRlciB0aGUgc3BlJ3MgZGlyZWN0bHkgCmZpbHRlcl9zcGUgPC0gZnVuY3Rpb24oc3BlKXsKICBzcGUgPC0gc3BlWywgc3BhdGlhbERhdGEoc3BlKSRpbl90aXNzdWUgPT0gMV0KfQoKYWxsX3NwZV9maWx0ZXIgPC0gYWxsX3NwZV9saXN0ICU+JQogIHB1cnJyOjptYXAoZmlsdGVyX3NwZSkKYGBgCgoKV2hlbiB3ZSBsb29rIGF0IG91ciByZXN1bHRzLCB3ZSB3aWxsIGFsc28gd2FudCB0byB2aXN1YWxpemUgdGhlbSBzbyB3ZSB3aWxsIG1ha2UgYSBjdXN0b20gZnVuY3Rpb24gdG8gcGxvdCB0aGUgcmVzdWx0cy4KCmBgYHtyfQojIGN1c3RvbSBmdW5jdGlvbiBmb3IgcGxvdHRpbmcgc3BlIHJlc3VsdHMgYW5kIGNvbG9yaW5nIGJ5IGNvbHVtbiBvZiBjb2xEYXRhIG9mIGNob2ljZQpwbG90X3NwZSA8LSBmdW5jdGlvbihzcGUsIHNhbXBsZSwgY29sdW1uKXsKICAjIHBsb3Qgc3BvdHMgb25seSAKICBwMSA8LSBnZ3NwYXZpczo6cGxvdFNwb3RzKHNwZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB4X2Nvb3JkID0gInB4bF9jb2xfaW5fZnVsbHJlcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgeV9jb29yZCA9ICJweGxfcm93X2luX2Z1bGxyZXMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRlID0gY29sdW1uKSArCiAgICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKQogIAogICMgcGxvdCB3aXRoIHRpc3N1ZSB1bmRlcm5lYXRoCiAgcDIgPC0gZ2dzcGF2aXM6OnBsb3RWaXNpdW0oc3BlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4X2Nvb3JkID0gInB4bF9jb2xfaW5fZnVsbHJlcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHlfY29vcmQgPSAicHhsX3Jvd19pbl9mdWxscmVzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGNvbHVtbikgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKQogIAogICMgYXJyYW5nZSBwbG90cyBhbmQgYWRkIHNhbXBsZSBuYW1lIGFzIHRpdGxlIAogIGdyaWQuYXJyYW5nZShwMSwgcDIsIG5yb3cgPSAxLCB0b3AgPSBncmlkOjp0ZXh0R3JvYihzYW1wbGUpKQp9CmBgYAoKRmlyc3Qgd2Ugd2lsbCBsb29rIGF0IHRoZSBwZXIgY2VsbCBtZXRyaWNzOiBtaXRvY2hvbmRyaWFsIHJlYWRzIHBlciBjZWxsLCB0b3RhbCBVTUkgcGVyIGNlbGwsIGFuZCB0b3RhbCBnZW5lcyBkZXRlY3RlZCBwZXIgY2VsbC4gCgpgYGB7cn0KIyAlIG1pdG9jaG9uZHJpYWwgcmVhZHMvIHNwb3QgCmdncGxvdChjb2xkYXRhX2RmX2NvbW1vbiwgYWVzKHggPSB0b29sLCB5ID0gc3Vic2V0c19taXRvX3BlcmNlbnQsIGZpbGwgPSB0b29sKSkgKyAKICBnZW9tX2JveHBsb3QoKSArCiAgZmFjZXRfd3JhcCh+IHNhbXBsZV9pZCkgKwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsgCiAgeWxhYigiTWl0byBQZXJjZW50IikgKyAKICB4bGFiKCIiKQpgYGAKCmBgYHtyfQphbGxfc3BlX2ZpbHRlciAlPiUKICBwdXJycjo6aXdhbGsocGxvdF9zcGUsIGNvbHVtbiA9ICJzdWJzZXRzX21pdG9fcGVyY2VudCIpCmBgYApPdmVyYWxsIGl0IGxvb2tzIGxpa2UgbWl0b2Nob25kcmlhbCBjb250ZW50IGlzIGxvdyBhbmQgZmFpcmx5IHNpbWlsYXIgYWNyb3NzIGJvdGggdG9vbHMuIAoKYGBge3J9CiMgdG90YWwgVU1JLyBzcG90IApnZ3Bsb3QoY29sZGF0YV9kZl9jb21tb24sIGFlcyh4ID0gc3VtLCBjb2xvciA9IHRvb2wpKSArIAogIGdlb21fZGVuc2l0eSgpICsgCiAgZmFjZXRfd3JhcCh+IHNhbXBsZV9pZCkgKwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsgCiAgeWxhYigiVU1JL3Nwb3QiKSArIAogIHhsYWIoIiIpCmBgYAoKYGBge3J9CmFsbF9zcGVfZmlsdGVyICU+JQogIHB1cnJyOjppd2FsayhwbG90X3NwZSwgY29sdW1uID0gInN1bSIpCmBgYAoKCmBgYHtyfQojIHRvdGFsIGdlbmVzLyBzcG90IApnZ3Bsb3QoY29sZGF0YV9kZl9jb21tb24sIGFlcyh4ID0gZGV0ZWN0ZWQsIGNvbG9yID0gdG9vbCkpICsgCiAgZ2VvbV9kZW5zaXR5KCkgKwogIGZhY2V0X3dyYXAofiBzYW1wbGVfaWQpICsKICB0aGVtZV9jbGFzc2ljKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArIAogIHlsYWIoIkdlbmVzIGRldGVjdGVkL3Nwb3QiKSArIAogIHhsYWIoIiIpIApgYGAKCmBgYHtyfQphbGxfc3BlX2ZpbHRlciAlPiUKICBwdXJycjo6aXdhbGsocGxvdF9zcGUsIGNvbHVtbiA9ICJkZXRlY3RlZCIpCmBgYAoKR2VuZXJhbGx5IGl0IGxvb2tzIGxpa2UgdGhlIHRvb2xzIGFyZSBmYWlybHkgc2ltaWxhciwgZXhjZXB0IHRoYXQgQWxldmluLWZyeSBzaG93cyBhIHNsaWdodCBkZWNyZWFzZSBpbiBib3RoIHRvdGFsIFVNSS9jZWxsIGFuZCBnZW5lcyBkZXRlY3RlZC9jZWxsIHdoZW4gY29tcGFyZWQgdG8gU3BhY2VyYW5nZXIgYWxvbmUsIHdoaWNoIGlzIHNlZW4gaW4gdGhlIHNwYXRpYWwgcGxvdCBhcyB3ZWxsLiAKQWxldmluLWZyeS1rbmVlIGFuZCBhbGV2aW4tZnJ5LXVuZmlsdGVyZWQgc2VlbSB0byBhbG1vc3QgY29tcGxldGVseSBvdmVybGFwIGluIHRlcm1zIG9mIHF1YW50aWZpY2F0aW9uIHdpdGggdGhlIG9ubHkgZGlmZmVyZW5jZSBiZWluZyBhIGZldyBzcG90cyB0aGF0IHdlcmUgbm90IGRldGVjdGVkIHVzaW5nIHRoZSBrbmVlIG1ldGhvZCBhcmUgbm93IGlkZW50aWZpZWQgaW4gdGhlIHVuZmlsdGVyZWQgbWV0aG9kLiAKCgojIyMgUGVyIEdlbmUgUUMgTWV0cmljcwoKTGV0J3MgYWxzbyBsb29rIGF0IHRoZSBjb3JyZWxhdGlvbiBvZiBtZWFuIGdlbmUgZXhwcmVzc2lvbiBhY3Jvc3Mgc2hhcmVkIGdlbmVzLiAKV2Ugd2lsbCBmaXJzdCBuZWVkIHRvIGNhbGN1bGF0ZSB0aGUgcGVyIGZlYXR1cmUgUUMgb24gdGhlIGZpbHRlcmVkIHNwZXMgYWZ0ZXIgcmVtb3Zpbmcgc3BvdHMgbm90IHByZXNlbnQgaW4gdGhlIHRpc3N1ZSBhbmQgdGhlbiBncmFiIHRoZSBgcm93RGF0YWAgYW5kIGNvbWJpbmUgaW50byBhIGRhdGEgZnJhbWUgdXNlZCBmb3IgcGxvdHRpbmcuIAoKYGBge3J9CmFsbF9zcGVfZmlsdGVyIDwtIGFsbF9zcGVfZmlsdGVyICU+JQogIHB1cnJyOjptYXAoc2N1dHRsZTo6YWRkUGVyRmVhdHVyZVFDTWV0cmljcykKYGBgCgpgYGB7cn0KIyBncmFiIHJvd2RhdGEgYW5kIGNvbWJpbmUgd2l0aCBzYW1wbGUgaW5mbwpyb3dkYXRhX2RmIDwtIHB1cnJyOjptYXBfZGYoYWxsX3NwZV9maWx0ZXIsIHNjcGNhVG9vbHM6OnJvd2RhdGFfdG9fZGYsIC5pZCA9ICJ0b29sIikgJT4lCiAgIyBleHRyYWN0IHNhbXBsZV9pZCBmcm9tIHRvb2wgYW5kIGNyZWF0ZSBhIG5ldyBjb2x1bW4gdG8gYXZvaWQgZHVwbGljYXRlcwogIGRwbHlyOjptdXRhdGUoc2FtcGxlX2lkID0gc3RyaW5ncjo6d29yZCh0b29sLCAxLCBzZXAgPSAiLSIpLAogICAgICAgICAgICAgICAgdG9vbCA9IGRwbHlyOjpjYXNlX3doZW4odG9vbCAlaW4lIGZyeV9rbmVlX25hbWVzIH4gImFsZXZpbi1mcnkta25lZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b29sICVpbiUgZnJ5X3VuZmlsdGVyZWRfbmFtZXMgfiAiYWxldmluLWZyeS11bmZpbHRlcmVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvb2wgJWluJSBzcGFjZXJhbmdlcl9uYW1lcyB+ICJzcGFjZXJhbmdlciIpKSAlPiUKICBkcGx5cjo6bGVmdF9qb2luKHNhbXBsZV9pbmZvX2RmLAogICAgICAgICAgICAgICAgICAgYnkgPSBjKCJ0b29sIiwgInNhbXBsZV9pZCIgPSAic2FtcGxlIikpCmBgYAoKV2UgdGhlbiB3YW50IHRvIGZpbHRlciBvdXQgYW55IGxvd2x5IGRldGVjdGVkIGdlbmVzLCAoZGV0ZWN0ZWQgPCA1LjApIGFuZCByZXN0cmljdCBvdXIgYW5hbHlzaXMgdG8gdGhvc2UgZ2VuZXMgdGhhdCBhcmUgZm91bmQgaW4gYm90aCB0b29scy4gCgpgYGB7cn0KZ2VuZV9jb3VudHMgPC0gcm93ZGF0YV9kZiAlPiUgCiAgIyByZW1vdmUgZ2VuZXMgdGhhdCBoYXZlIGEgbG93IGZyZXF1ZW5jeSBvZiBiZWluZyBkZXRlY3RlZAogIGRwbHlyOjpmaWx0ZXIoZGV0ZWN0ZWQgPj0gNS4wKSAlPiUKICBkcGx5cjo6Y291bnQoZ2VuZV9pZCwgc2FtcGxlX2lkKQoKIyByZXN0cmljdCB0byBvbmx5IGNvbW1vbiBnZW5lcyAKY29tbW9uX2dlbmVzIDwtIGdlbmVfY291bnRzICU+JQogIGRwbHlyOjpmaWx0ZXIobiA9PSAzKSAlPiUKICBkcGx5cjo6cHVsbChnZW5lX2lkKQoKcm93ZGF0YV9kZl9jb21tb24gPC0gcm93ZGF0YV9kZiAlPiUKICBkcGx5cjo6ZmlsdGVyKGdlbmVfaWQgJWluJSBjb21tb25fZ2VuZXMpIApgYGAKCmBgYHtyfQojIGNyZWF0ZSBhIHRhYmxlIHRvIGNhbGN1bGF0ZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIG1lYW4gZ2VuZSBleHByZXNzaW9uCnJvd2RhdGFfY29yIDwtIHJvd2RhdGFfZGZfY29tbW9uICU+JQogIGRwbHlyOjpzZWxlY3QodG9vbCwgZ2VuZV9pZCwgc2FtcGxlX2lkLCBtZWFuKSAlPiUKICAjIHNwcmVhZCB0aGUgbWVhbiBleHByZXNzaW9uIHN0YXRzIHRvIG9uZSBjb2x1bW4gcGVyIGNhbGxlcgogIHRpZHlyOjpwaXZvdF93aWRlcihpZF9jb2xzID0gYyhnZW5lX2lkLCBzYW1wbGVfaWQpLAogICAgICAgICAgICAgICAgICAgICBuYW1lc19mcm9tID0gYygidG9vbCIpLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IG1lYW4pICU+JQogICMgZHJvcCByb3dzIHdpdGggTkEgdmFsdWVzIHRvIGVhc2UgY29ycmVsYXRpb24gY2FsY3VsYXRpb24KICB0aWR5cjo6ZHJvcF9uYSgpCmBgYAoKYGBge3J9CiMgbG9vayBhdCBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSB0d28gdG9vbHMKcm93ZGF0YV9jb3IgJT4lIAogIGRwbHlyOjpncm91cF9ieShzYW1wbGVfaWQpICU+JQogIGRwbHlyOjpzdW1tYXJpemUoCiAgICBhbGV2aW5fZnJ5X2tuZWVfc3BhY2VyYW5nZXJfY29yID0gY29yKGBzcGFjZXJhbmdlcmAsIGBhbGV2aW4tZnJ5LWtuZWVgLCBtZXRob2QgPSAic3BlYXJtYW4iKSwKICAgIGFsZXZpbl9mcnlfdW5maWx0ZXJlZF9zcGFjZXJhbmdlcl9jb3I9IGNvcihgc3BhY2VyYW5nZXJgLCBgYWxldmluLWZyeS11bmZpbHRlcmVkYCwgbWV0aG9kID0gInNwZWFybWFuIikKICApCmBgYAoKYGBge3J9CiMgbWVhbiBnZW5lIGV4cHJlc3Npb24gYWNyb3NzIHNoYXJlZCBnZW5lcyAKZ2dwbG90KHJvd2RhdGFfY29yLCBhZXMoeCA9IGBzcGFjZXJhbmdlcmAsIHkgPSBgYWxldmluLWZyeS1rbmVlYCkpICsKICBnZW9tX3BvaW50KHNpemUgPSAwLjUsIGFscGhhID0gMC4xKSArIAogIHNjYWxlX3hfbG9nMTAoKSArIAogIHNjYWxlX3lfbG9nMTAoKSArIAogIGZhY2V0X3dyYXAofnNhbXBsZV9pZCkgKwogIGdlb21fYWJsaW5lKCkgKwogIGxhYnMoeCA9ICJTcGFjZXJhbmdlciBtZWFuIGdlbmUgZXhwcmVzc2lvbiIsIHkgPSAiQWxldmluIEZyeSBLbmVlIE1lYW4gZ2VuZSBleHByZXNzaW9uIikgKyAKICB0aGVtZV9jbGFzc2ljKCkKICAKYGBgCgpgYGB7cn0KZ2dwbG90KHJvd2RhdGFfY29yLCBhZXMoeCA9IGBzcGFjZXJhbmdlcmAsIHkgPSBgYWxldmluLWZyeS11bmZpbHRlcmVkYCkpICsKICBnZW9tX3BvaW50KHNpemUgPSAwLjUsIGFscGhhID0gMC4xKSArIAogIHNjYWxlX3hfbG9nMTAoKSArIAogIHNjYWxlX3lfbG9nMTAoKSArIAogIGZhY2V0X3dyYXAofnNhbXBsZV9pZCkgKwogIGdlb21fYWJsaW5lKCkgKwogIGxhYnMoeCA9ICJTcGFjZXJhbmdlciBtZWFuIGdlbmUgZXhwcmVzc2lvbiIsIHkgPSAiQWxldmluIEZyeSBVbmZpbHRlcmVkIE1lYW4gZ2VuZSBleHByZXNzaW9uIikgKyAKICB0aGVtZV9jbGFzc2ljKCkKYGBgCkNvcnJlbGF0aW9uIGFwcGVhcnMgdG8gYmUgcXVpdGUgaGlnaCBiZXR3ZWVuIG1lYW4gZ2VuZSBleHByZXNzaW9uIGluIFNwYWNlcmFuZ2VyIGFuZCBBbGV2aW4tZnJ5LCBob3dldmVyLCB3ZSBkbyBzZWUgdGhhdCBnZW5lcmFsbHkgZ2VuZXMgaGF2ZSBoaWdoZXIgZ2VuZSBleHByZXNzaW9uIGluIFNwYWNlcmFuZ2VyIHRoYW4gaW4gQWxldmluLWZyeSBhbmQgYXJlIHNsaWdodGx5IG9mZiB0aGUgZGlhZ29uYWwuIAoKVGhlcmUgaXMgYSBzdWJzZXQgb2YgZ2VuZXMgdGhhdCBzZWVtcyB0byBiZSBzbGlnaHRseSBtb3JlIGFmZmVjdGVkIGFuZCBiZSBmdXJ0aGVyIG9mZiB0aGUgZGlhZ29uYWwuIApMZXQncyB0YWtlIGF0IHRob3NlIGdlbmUgZm9yIGVhY2ggc2FtcGxlLiAKSXQgZG9lcyBhcHBlYXIgdG8gYmUgbGVzcyBkcmFtYXRpYyBpbiBTQ1BDUjAwMDM3My4KCmBgYHtyfQojIGdldCB0aGUgZ2VuZSBzeW1ib2xzIGFuZCB0aGVuIGpvaW4gYmFjayB3aXRoIHJvd2RhdGEgZGYgCmdlbmVfc3ltYm9sc19kZiA8LSByb3dkYXRhX2RmICU+JQogIGRwbHlyOjpzZWxlY3QoZ2VuZV9pZCwgc3ltYm9sKQoKIyBqb2luIGNvcnJlbGF0aW9uIHdpdGggZ2VuZSBzeW1ib2xzIApyb3dkYXRhX2NvciA8LSByb3dkYXRhX2NvciAlPiUKICBkcGx5cjo6bGVmdF9qb2luKGdlbmVfc3ltYm9sc19kZikgJT4lCiAgZHBseXI6OmRpc3RpbmN0KCkgJT4lCiAgdGlkeXI6OmRyb3BfbmEoKSAlPiUKICAjIGFkZCBkaWZmZXJlbmNlIGluIG1lYW4gZ2VuZSBleHByZXNzaW9uIGJldHdlZW4gYWxldmluLWZyeSBhbmQgc3BhY2VyYW5nZXIgCiAgZHBseXI6Om11dGF0ZShrbmVlX2xvZ19mb2xkX2NoYW5nZSA9IGxvZyhgYWxldmluLWZyeS1rbmVlYC9zcGFjZXJhbmdlciksCiAgICAgICAgICAgICAgICB1bmZpbHRlcmVkX2xvZ19mb2xkX2NoYW5nZSA9IGxvZyhgYWxldmluLWZyeS11bmZpbHRlcmVkYC9gc3BhY2VyYW5nZXJgKSkKYGBgCgoKTGV0J3Mgc2VlIGlmIHdlIGNhbiBzcGVjaWZpY2FsbCBpZGVudGlmeSB0aGUgZ3JvdXAgb2YgZ2VuZXMgdGhhdCBhcmUgb2ZmIHRoZSBkaWFnb25hbCBieSBsYWJlbGluZyB0aGVtIHdpdGggYSBkaWZmZXJlbnQgY29sb3IuIAoKYGBge3J9CnJvd2RhdGFfY29yIDwtIHJvd2RhdGFfY29yICU+JQogIGRwbHlyOjptdXRhdGUoa25lZV9kaWZmID0gaWZlbHNlKAogICAga25lZV9sb2dfZm9sZF9jaGFuZ2UgPCAtIDAuNzUgJiAKICAgICAgc3BhY2VyYW5nZXIgPCA1ICYgCiAgICAgIHNwYWNlcmFuZ2VyID4gLTEsICJkaWZmX2V4cHJlc3Npb24iLCAiZXF1YWxfZXhwcmVzc2lvbiIpLAogICAgdW5maWx0ZXJlZF9kaWZmID0gaWZlbHNlKAogICAgdW5maWx0ZXJlZF9sb2dfZm9sZF9jaGFuZ2UgPCAtIDAuNzUgJiAKICAgICAgc3BhY2VyYW5nZXIgPCA1ICYgCiAgICAgIHNwYWNlcmFuZ2VyID4gLTEsICJkaWZmX2V4cHJlc3Npb24iLCAiZXF1YWxfZXhwcmVzc2lvbiIpKQpgYGAKCgpgYGB7cn0KZ2dwbG90KHJvd2RhdGFfY29yLCBhZXMoeCA9IGBzcGFjZXJhbmdlcmAsIHkgPSBgYWxldmluLWZyeS1rbmVlYCwgY29sb3IgPSBrbmVlX2RpZmYpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC41LCBhbHBoYSA9IDAuMSkgKyAKICBzY2FsZV94X2xvZzEwKCkgKyAKICBzY2FsZV95X2xvZzEwKCkgKyAKICBnZW9tX2FibGluZSgpICsKICBmYWNldF93cmFwKH4gc2FtcGxlX2lkLCBucm93ID0gMikgKyAKICBsYWJzKHggPSAiU3BhY2VyYW5nZXIgbWVhbiBnZW5lIGV4cHJlc3Npb24iLCB5ID0gIkFsZXZpbiBGcnkgS25lZSBNZWFuIGdlbmUgZXhwcmVzc2lvbiIpICsgCiAgdGhlbWVfY2xhc3NpYygpCmBgYApgYGB7cn0KZ2dwbG90KHJvd2RhdGFfY29yLCBhZXMoeCA9IGBzcGFjZXJhbmdlcmAsIHkgPSBgYWxldmluLWZyeS11bmZpbHRlcmVkYCwgY29sb3IgPSB1bmZpbHRlcmVkX2RpZmYpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC41LCBhbHBoYSA9IDAuMSkgKyAKICBzY2FsZV94X2xvZzEwKCkgKyAKICBzY2FsZV95X2xvZzEwKCkgKyAKICBnZW9tX2FibGluZSgpICsKICBmYWNldF93cmFwKH4gc2FtcGxlX2lkLCBucm93ID0gMikgKyAKICBsYWJzKHggPSAiU3BhY2VyYW5nZXIgbWVhbiBnZW5lIGV4cHJlc3Npb24iLCB5ID0gIkFsZXZpbiBGcnkgVW5maWx0ZXJlZCBNZWFuIGdlbmUgZXhwcmVzc2lvbiIpICsgCiAgdGhlbWVfY2xhc3NpYygpCmBgYAoKSXQgbG9va3MgbGlrZSB0aGUgZ2VuZXMgdGhhdCB0aGlzIHNlcGFyYXRlIGdyb3VwIG9mIGdlbmVzIGhhdmUgYSBsb2coZm9sZCBjaGFuZ2UpIDwgLTAuNzUgYW5kIG1lYW4gZ2VuZSBleHByZXNzaW9uIGluIHNwYWNlcmFuZ2VyIDwgNSBhbmQgPiAtMS4gCkxldCdzIGdldCB0aGF0IGxpc3Qgb2YgZ2VuZXMgaW4gYm90aCBhbGV2aW4tZnJ5LWtuZWUgYW5kIGFsZXZpbi1mcnktdW5maWx0ZXJlZC4gCgpgYGB7cn0Ka25lZV9kaWZmX2dlbmVfY291bnRzIDwtIHJvd2RhdGFfY29yICU+JQogIGRwbHlyOjpmaWx0ZXIoa25lZV9kaWZmID09ICJkaWZmX2V4cHJlc3Npb24iKSAlPiUKICBkcGx5cjo6Y291bnQoZ2VuZV9pZCkgJT4lIAogIGRwbHlyOjpmaWx0ZXIobiA9PSAyKSAlPiUKICBkcGx5cjo6cHVsbChnZW5lX2lkKQoKa25lZV9kaWZmX2dlbmVzIDwtIHJvd2RhdGFfY29yICU+JQogIGRwbHlyOjpmaWx0ZXIoZ2VuZV9pZCAlaW4lIGtuZWVfZGlmZl9nZW5lX2NvdW50cykgJT4lCiAgZHBseXI6OmFycmFuZ2Uoc3ltYm9sKQoKa25lZV9kaWZmX2dlbmVzCmBgYApgYGB7cn0KdW5maWx0ZXJlZF9kaWZmX2dlbmVfY291bnRzIDwtIHJvd2RhdGFfY29yICU+JQogIGRwbHlyOjpmaWx0ZXIodW5maWx0ZXJlZF9kaWZmID09ICJkaWZmX2V4cHJlc3Npb24iKSAlPiUKICBkcGx5cjo6Y291bnQoZ2VuZV9pZCkgJT4lIAogIGRwbHlyOjpmaWx0ZXIobiA9PSAyKSAlPiUKICBkcGx5cjo6cHVsbChnZW5lX2lkKQoKdW5maWx0ZXJlZF9kaWZmX2dlbmVzIDwtIHJvd2RhdGFfY29yICU+JQogIGRwbHlyOjpmaWx0ZXIoZ2VuZV9pZCAlaW4lIHVuZmlsdGVyZWRfZGlmZl9nZW5lX2NvdW50cykgJT4lCiAgZHBseXI6OmFycmFuZ2Uoc3ltYm9sKQoKdW5maWx0ZXJlZF9kaWZmX2dlbmVzCmBgYApMZXQncyBsb29rIGF0IHdoYXQgdHlwZXMgb2YgZ2VuZXMgYXJlIGZvdW5kIHRvIGhhdmUgZGlmZmVyZW50IGdlbmUgZXhwcmVzc2lvbiBhY3Jvc3MgdGhlc2UgdHdvIHRvb2xzIHVzaW5nIG92ZXIgcmVwcmVzZW50YXRpb24gYW5hbHlzaXMuIApGaXJzdCwgbGV0J3MgbG9vayBzcGVjaWZpY2FsbHkgYXQgdGhhdCBncm91cCBvZiBnZW5lcyB0aGF0IGlzIHNsaWdodGx5IG9mZiB0aGUgZGlhZ29uYWwgKGNvbnNpZGVyaW5nIG9ubHkgZ2VuZXMgZm91bmQgaW4gYm90aCBzYW1wbGVzKSwgdGhlbiB3ZSB3aWxsIGxvb2sgYXQgYWxsIGdlbmVzIHdpdGggYWJzKGxvZyhmb2xkIGNoYW5nZSkgPiAwLjUpCgpgYGB7cn0KIyB1bmZpbHRlcmVkIGFuZCBrbmVlIG91dGxpZXIgZ2VuZSBsaXN0cwprbmVlX2RpZmZfZ2VuZXMgPC0ga25lZV9kaWZmX2dlbmVzJHN5bWJvbCAlPiUKICB1bmlxdWUoKQoKdW5maWx0ZXJlZF9kaWZmX2dlbmVzIDwtIHVuZmlsdGVyZWRfZGlmZl9nZW5lcyRzeW1ib2wgJT4lCiAgdW5pcXVlKCkKCiMgYmFja2dyb3VuZCBnZW5lIGxpc3QgCmJhY2tncm91bmRfZ2VuZXMgPC0gcm93ZGF0YV9jb3Ikc3ltYm9sICU+JQogIHVuaXF1ZSgpCmBgYAoKYGBge3J9CmtuZWVfZ29fb3JhX3Jlc3VsdHMgPC0gZW5yaWNoR08oZ2VuZSA9IGtuZWVfZGlmZl9nZW5lcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bml2ZXJzZSA9IGJhY2tncm91bmRfZ2VuZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2V5VHlwZSA9ICJTWU1CT0wiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9yZ0RiID0gb3JnLkhzLmVnLmRiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9udCA9ICJCUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcEFkanVzdE1ldGhvZCA9ICJCSCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmID0gMC4wMDAwMSkKCnVuZmlsdGVyZWRfZ29fb3JhX3Jlc3VsdHMgPC0gZW5yaWNoR08oZ2VuZSA9IHVuZmlsdGVyZWRfZGlmZl9nZW5lcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bml2ZXJzZSA9IGJhY2tncm91bmRfZ2VuZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2V5VHlwZSA9ICJTWU1CT0wiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9yZ0RiID0gb3JnLkhzLmVnLmRiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9udCA9ICJCUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcEFkanVzdE1ldGhvZCA9ICJCSCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmID0gMC4wMDAwMSkKYGBgCgpgYGB7cn0Ka25lZV9nb19yZXN1bHRzIDwtIGtuZWVfZ29fb3JhX3Jlc3VsdHNAcmVzdWx0ICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBkcGx5cjo6ZmlsdGVyKHAuYWRqdXN0IDwgMC4yKQprbmVlX2dvX3Jlc3VsdHMKYGBgCgpgYGB7cn0KdW5maWx0ZXJlZF9nb19yZXN1bHRzIDwtIHVuZmlsdGVyZWRfZ29fb3JhX3Jlc3VsdHNAcmVzdWx0ICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBkcGx5cjo6ZmlsdGVyKHAuYWRqdXN0IDwgMC4yKQp1bmZpbHRlcmVkX2dvX3Jlc3VsdHMKYGBgCkluIG9yZGVyIHRvIHNlZSBhbnkgZW5yaWNobWVudCByZXN1bHRzLCB0aGUgYWRqdXN0ZWQgcC12YWx1ZSB0aHJlc2hvbGQgaGFzIHRvIGJlIGluY3JlYXNlZCB0byAwLjIgc28gdGhlcmUgaXMgdmVyeSBsaXR0bGUgY29uZmlkZW5jZSB0aGF0IHRoZXNlIHBhdGh3YXlzIGFyZSBhY3R1YWxseSBlbnJpY2hlZC4gClRoZXJlZm9yZSwgaXQgYXBwZWFycyB0aGF0IHRoZXJlIGFyZSBubyBzaWduaWZpY2FudCBwYXRod2F5cyB0aGF0IGFyZSBhZmZlY3RlZCBieSB0aGUgZ3JvdXAgb2YgZ2VuZXMgdGhhdCBhcmUgZm91bmQgdG8gYmUgb2ZmIHRoZSBkaWFnb25hbCBiZXR3ZWVuIHNwYWNlcmFuZ2VyIGFuZCBhbGV2aW4tZnJ5LiAKCkxldCdzIHRha2UgYSBsb29rIGF0IGFsbCBnZW5lcyB0aGF0IGhhdmUgYXQgbGVhc3QgMC41IGxvZyBmb2xkIGNoYW5nZSBiZXR3ZWVuIFNwYWNlcmFuZ2VyIGFuZCBBbGV2aW4tZnJ5LiAKCmBgYHtyfQojIGZpbmQgZ2VuZXMgdGhhdCBoYXZlIGRpZmZlcmVudCBnZW5lIGV4cHJlc3Npb24gaW4gYm90aCBzYW1wbGVzIGJldHdlZW4gc3BhY2VyYW5nZXIgYW5kIGZyeS1rbmVlIG9yIGZyeS11bmZpbHRlcmVkIApkaWZmZXJlbnRfZ2VuZV9jb3VudHMgPC0gcm93ZGF0YV9jb3IgJT4lCiAgZHBseXI6OmZpbHRlcihhYnMoa25lZV9sb2dfZm9sZF9jaGFuZ2UpID4gMC41IHwgYWJzKHVuZmlsdGVyZWRfbG9nX2ZvbGRfY2hhbmdlKSA+IDAuNSkgJT4lCiAgZHBseXI6OmNvdW50KGdlbmVfaWQpICU+JSAKICBkcGx5cjo6ZmlsdGVyKG4gPT0gMikgJT4lCiAgZHBseXI6OnB1bGwoZ2VuZV9pZCkKYGBgCgpgYGB7cn0KIyBwcmludCBvdXQgbGlzdCBvZiBnZW5lcyB3aXRoIHdpdGggPiAxLjUgZ2VuZSBleHByZXNzaW9uIGluIEFsZXZpbi1mcnkgCiMgb25seSBpbmNsdWRlIGdlbmVzIHRoYXQgYXJlIGZvdW5kIGluIGJvdGggc2FtcGxlcyAKcm93ZGF0YV9jb3IgJT4lCiAgZHBseXI6OmZpbHRlcihnZW5lX2lkICVpbiUgZGlmZmVyZW50X2dlbmVfY291bnRzKSAlPiUKICBkcGx5cjo6YXJyYW5nZShzeW1ib2wpICU+JQogIGRwbHlyOjpzZWxlY3Qoc2FtcGxlX2lkLCBzeW1ib2wsIGdlbmVfaWQsIGtuZWVfbG9nX2ZvbGRfY2hhbmdlLCAKICAgICAgICAgICAgICAgIHVuZmlsdGVyZWRfbG9nX2ZvbGRfY2hhbmdlLCBgYWxldmluLWZyeS1rbmVlYCwgCiAgICAgICAgICAgICAgICBgYWxldmluLWZyeS11bmZpbHRlcmVkYCwgc3BhY2VyYW5nZXIpCmBgYAoKCmBgYHtyfQojIGV4dHJhY3QgdGFyZ2V0IGdlbmUgbGlzdCBmb3IgT1JBLCA4ODYgZ2VuZXMKZGlmZmVyZW50X2dlbmVzIDwtIHJvd2RhdGFfY29yICU+JQogICMgZmlsdGVyIGZvciBhbnl0aGluZyB3aXRoIGZvbGQgY2hhbmdlID4gMS41IGFuZCBmb3VuZCBpbiBib3RoIHNhbXBsZXMKICBkcGx5cjo6ZmlsdGVyKGdlbmVfaWQgJWluJSBkaWZmZXJlbnRfZ2VuZV9jb3VudHMpICU+JQogIGRwbHlyOjpwdWxsKHN5bWJvbCkgJT4lCiAgdW5pcXVlKCkKYGBgCgpXZSBhcmUgb25seSBnb2luZyB0byB1c2Ugb25lIGxpc3Qgb2YgZ2VuZXMgaGVyZSB0byBkbyBvdmVyIHJlcHJlc2VudGF0aW9uIGFuYWx5c2lzLCBiZWNhdXNlIHRoZSBzYW1lIGdlbmVzIGFyZSBmb3VuZCB0byBoYXZlIGRpZmZlcmVudCBnZW5lIGV4cHJlc3Npb24gYmV0d2VlbiBBbGV2aW4tZnJ5IGFuZCBzcGFjZXJhbmdlciByZWdhcmRsZXNzIG9mIGZpbHRlcmluZyBzdHJhdGVneSBmb3IgQWxldmluLWZyeS4gCgpgYGB7cn0KIyBwZXJmb3JtIGdlbmUgb250b2xvZ3kgbG9va2luZyBhdCBhbGwgZ2VuZXMgdGhhdCBhcmUgZGlmZmVyZW50IApnb19vcmFfcmVzdWx0cyA8LSBlbnJpY2hHTyhnZW5lID0gZGlmZmVyZW50X2dlbmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICB1bml2ZXJzZSA9IGJhY2tncm91bmRfZ2VuZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGtleVR5cGUgPSAiU1lNQk9MIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgT3JnRGIgPSBvcmcuSHMuZWcuZGIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG9udCA9ICJCUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHBBZGp1c3RNZXRob2QgPSAiQkgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgPSAwLjAwMDAxKQpgYGAKCgpgYGB7cn0KIyBsb29rIGF0IGdlbmUgb250b2xvZ3kgcmVzdWx0cyAKZ29fcmVzdWx0cyA8LSBnb19vcmFfcmVzdWx0c0ByZXN1bHQgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIGRwbHlyOjpmaWx0ZXIocC5hZGp1c3QgPCAwLjIpCmdvX3Jlc3VsdHMKYGBgCgpJdCBsb29rcyBsaWtlIHdoZW4geW91IGZpbHRlciB0byBvbmx5IGluY2x1ZGUgZ2VuZXMgdGhhdCBhcmUgZm91bmQgdG8gaGF2ZSBkaWZmZXJlbnQgbWVhbiBnZW5lIGV4cHJlc3Npb24gYmV0d2VlbiBTcGFjZXJhbmdlciBhbmQgQWxldmluLWZyeSBpbiBib3RoIHNhbXBsZXMgdGhlcmUgYXJlIG5vIHNwZWNpZmljIHBhdGh3YXlzIGlkZW50aWZpZWQgdGhhdCB0aGUgZ2VuZXMgYmVsb25nIHRvLiAKCiMjIyBTaGFyZWQgZ2VuZXMgYWNyb3NzIHRvb2xzCgpUaGUgZmluYWwgdGhpbmcgd2Ugd2lsbCBsb29rIGF0IGlzIHRoZSBvdmVybGFwIG9mIGdlbmVzIGRldGVjdGVkIGluIGVhY2ggdG9vbC4gCkZpcnN0IHdlIG5lZWQgdG8gZmlsdGVyIHRoZSBgU3BhdGlhbEV4cGVyaW1lbnRgIG9iamVjdHMgdG8gb25seSBoYXZlIHNwb3RzIHRoYXQgYXJlIGZvdW5kIGluIGJvdGggdG9vbHMgYW5kIHRoZW4gd2UgZmlsdGVyIHRvIGluY2x1ZGUgb25seSBnZW5lcyB0aGF0IGFyZSBmb3VuZCBpbiBib3RoIGluZGljZXMgYmVmb3JlIGxvb2tpbmcgYXQgdGhlIG92ZXJsYXAuIAoKYGBge3J9CiMgbWFrZSBhIGZ1bmN0aW9uIHRvIGZpbHRlciBzY2VzIGJ5IHN1YnNldCBvZiBzcG90cyBhbmQgcmUtY2FsY3VsYXRlIGZlYXR1cmUgc3RhdHMKZmlsdGVyX3NwZSA8LSBmdW5jdGlvbihzcGUsIHNwb3RzKXsKICAjIHJlbW92ZSAiLTEiIGF0IGVuZCBvZiBiYXJjb2RlIGZvciBzcGFjZXJhbmdlciBzcGVzCiAgY29sbmFtZXMoc3BlKSA8LSBnc3ViKCItMSIsICIiLCBjb2xuYW1lcyhzcGUpKQogIGNlbGxzX3RvX2tlZXAgPC0gY29sbmFtZXMoc3BlKSAlaW4lIHNwb3RzCiAgcm93RGF0YShzcGUpIDwtIE5VTEwKICBzcGVbLCBjZWxsc190b19rZWVwXSAlPiUKICAgIHNjdXR0bGU6OmFkZFBlckZlYXR1cmVRQ01ldHJpY3MoKQp9CmBgYAoKYGBge3J9CiMgZmlsdGVyIGFsbCBzcGVzIHRvIG9ubHkgaGF2ZSBjb21tb24gc3BvdHMgCnNwZV9saXN0X2NvbW1vbiA8LSBhbGxfc3BlX2ZpbHRlciAlPiUKICBwdXJycjo6bWFwKGZpbHRlcl9zcGUsIHNwb3RzID0gY29tbW9uX3Nwb3RzKQoKIyBncmFiIHJvd2RhdGEgZnJvbSBmaWx0ZXJlZCBzY2VzIApyb3dkYXRhX2RmX2ZpbHRlcmVkIDwtIHB1cnJyOjptYXBfZGYoc3BlX2xpc3RfY29tbW9uLCBzY3BjYVRvb2xzOjpyb3dkYXRhX3RvX2RmLCAuaWQgPSAidG9vbCIpICU+JQogIGRwbHlyOjptdXRhdGUoc2FtcGxlX2lkID0gc3RyaW5ncjo6d29yZCh0b29sLCAxLCBzZXAgPSAiLSIpLAogICAgICAgICAgICAgICAgdG9vbCA9IGRwbHlyOjpjYXNlX3doZW4odG9vbCAlaW4lIGZyeV9rbmVlX25hbWVzIH4gImFsZXZpbi1mcnkta25lZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b29sICVpbiUgZnJ5X3VuZmlsdGVyZWRfbmFtZXMgfiAiYWxldmluLWZyeS11bmZpbHRlcmVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvb2wgJWluJSBzcGFjZXJhbmdlcl9uYW1lcyB+ICJzcGFjZXJhbmdlciIpKSAlPiUKICBkcGx5cjo6bGVmdF9qb2luKHNhbXBsZV9pbmZvX2RmLAogICAgICAgICAgICAgICAgICAgYnkgPSBjKCJ0b29sIiwgInNhbXBsZV9pZCIgPSAic2FtcGxlIikpCmBgYAoKCmBgYHtyfQojIGdldCBnZW5lcyBjb21tb24gaW4gYWxsIHRvb2xzCmNvbW1vbl9nZW5lcyA8LSByb3dkYXRhX2RmX2ZpbHRlcmVkICU+JQogIGRwbHlyOjpzZWxlY3QoZ2VuZV9pZCwgdG9vbCkgJT4lCiAgZHBseXI6OmRpc3RpbmN0KCkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGdlbmVfaWQpICU+JQogIGRwbHlyOjp0YWxseSgpICU+JQogICMgZmlsdGVyIGZvciBnZW5lcyBmb3VuZCBpbiBib3RoIHNwYWNlcmFuZ2VyIGFuZCBhbGV2aW4tZnJ5CiAgZHBseXI6OmZpbHRlcihuID09IDMpICU+JQogIGRwbHlyOjpwdWxsKGdlbmVfaWQpCgojIGZpbHRlciByb3dkYXRhX2RmIHRvIG9ubHkgaW5jbHVkZSBnZW5lcyBmb3VuZCBpbiBhbGwgdG9vbHMgYW5kIGdlbmVzIHdpdGggbWVhbiA+IDAgYW5kIGRldGVjdGVkID4gMCBpbiBhbGwgY2VsbHMKcm93ZGF0YV9kZl9maWx0ZXJlZCA8LSByb3dkYXRhX2RmX2ZpbHRlcmVkICU+JSAKICBkcGx5cjo6ZmlsdGVyKGdlbmVfaWQgJWluJSBjb21tb25fZ2VuZXMpICU+JQogIGRwbHlyOjpmaWx0ZXIobWVhbiA+IDAgJiBkZXRlY3RlZCA+IDApCmBgYAoKCmBgYHtyfQojIGNyZWF0ZSB1cHNldCBwbG90IApnZW5lX2RldGVjdF9kZiA8LSByb3dkYXRhX2RmX2ZpbHRlcmVkICU+JQogIGRwbHlyOjpncm91cF9ieShnZW5lX2lkKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKHRvb2xzX2RldGVjdGVkID0gbGlzdCh1bmlxdWUodG9vbCkpKQoKZ2dwbG90KGdlbmVfZGV0ZWN0X2RmLCBhZXMoeCA9IHRvb2xzX2RldGVjdGVkKSkgKwogIGdlb21fYmFyKCkgKwogIHNjYWxlX3hfdXBzZXQobl9pbnRlcnNlY3Rpb25zID0gNCkKYGBgCgpGb3IgdGhlIG1vc3QgcGFydCBnZW5lcyBhcmUgZm91bmQgaW4gYWxsIHRvb2xzLCBob3dldmVyIGl0IGxvb2tzIGxpa2UgdGhlcmUgaXMgYSBjaHVuayBvZiBnZW5lcyB0aGF0IHdlIHdvdWxkIG1pc3MgdGhhdCBhcmUgZm91bmQgaW4gU3BhY2VyYW5nZXIgdGhhdCBhcmUgbm90IGZvdW5kIGluIGVpdGhlciBvZiB0aGUgQWxldmluLWZyeSB0b29scy4gCkFyZSBhbnkgb2YgdGhvc2UgZ2VuZXMgaW52b2x2ZWQgaW4gaW1wb3J0YW50IHBhdGh3YXlzIHRoYXQgd2UgbWlnaHQgd2FudCB0byBtYWtlIHN1cmUgdGhhdCB3ZSBkb24ndCBsb3NlPyAKRmlyc3QsIHdlIGNhbiBzZWUgd2hhdCB0eXBlcyBvZiBnZW5lcyB0aGVyZSBhcmUgYnkgZ3JhYmJpbmcgdGhlIGdlbmUgYW5ub3RhdGlvbiBmcm9tIHRoZSBndGYgZmlsZS4gCgpgYGB7cn0KIyBsb2FkIGd0ZiBmaWxlIGFuZCBzZWxlY3QgZ2VuZSBuYW1lIGFuZCBnZW5lX2Jpb3R5cGUKZ3RmX2ZpbGUgPC0gZmlsZS5wYXRoKGJhc2VfZGlyLCAic2FtcGxlLWluZm8iLCAiSG9tb19zYXBpZW5zLkdSQ2gzOC4xMDQuZ3RmLmd6IikKZ3RmIDwtIHJ0cmFja2xheWVyOjppbXBvcnQoZ3RmX2ZpbGUsIGZlYXR1cmUudHlwZSA9ICJnZW5lIikgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIGRwbHlyOjpzZWxlY3QoZ2VuZV9pZCwgZ2VuZV9uYW1lLCBnZW5lX2Jpb3R5cGUpCmBgYAoKYGBge3J9CiMgam9pbiBzcGFjZXJhbmdlciBvbmx5IGdlbmVzIHdpdGggZ2VuZSBiaW90eXBlcyBmcm9tIGd0ZgpzcGFjZXJhbmdlcl9vbmx5X2dlbmVfdGFibGUgPC0gZ2VuZV9kZXRlY3RfZGYgJT4lCiAgZHBseXI6OmZpbHRlcih0b29sc19kZXRlY3RlZCA9PSAic3BhY2VyYW5nZXIiKSAlPiUKICBkcGx5cjo6c2VsZWN0KGdlbmVfaWQpICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4oZ3RmLCBieT0gImdlbmVfaWQiKSAKCiMgY3JlYXRlIGEgdGFibGUgb2YgZ2VuZSBiaW90eXBlcyB0aGF0IGFyZSBzcGVjaWZpYyB0byBzcGFjZXJhbmdlcgpzcGFjZXJhbmdlcl9nZW5lX2NvdW50cyA8LSBzcGFjZXJhbmdlcl9vbmx5X2dlbmVfdGFibGUgJT4lCiAgZHBseXI6OmNvdW50KGdlbmVfYmlvdHlwZSkgJT4lIAogICMgcmVvcmRlciBieSBnZW5lIGJpb3R5cGUKICBkcGx5cjo6bXV0YXRlKGdlbmVfYmlvdHlwZSA9ZmFjdG9yKGdlbmVfYmlvdHlwZSwgbGV2ZWxzID0gZ2VuZV9iaW90eXBlW29yZGVyKG4pXSkpCmBgYAoKYGBge3J9CmdncGxvdChzcGFjZXJhbmdlcl9nZW5lX2NvdW50cywgYWVzKHkgPSBnZW5lX2Jpb3R5cGUsIHggPSBuKSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyAKICB4bGFiKCIiKQpgYGAKSXQgbG9va3MgbGlrZSB0aGUgbWFqb3JpdHkgb2YgdGhlIGdlbmVzIHRoYXQgd2UgbG9zZSBmcm9tIG5vdCB1c2luZyBTcGFjZXJhbmdlciBjb3JyZXNwb25kIHRvIHBzZXVkb2dlbmVzIGFuZCBsb25nLW5vbmNvZGluZyBSTkEncywgd2l0aCBwcm90ZWluIGNvZGluZyBnZW5lcyBiZWluZyB0aGUgdGhpcmQgbW9zdCByZXByZXNlbnRlZCBjYXRlZ29yeS4gCldlIGNhbiBhbHNvIGRvIG92ZXItcmVwcmVzZW50YXRpb24gYW5hbHlzaXMgYW5kIHNlZSBpZiBhbnkgZ2VuZXMgaW4gc3BlY2lmaWMgcGF0aHdheXMgYXJlIGxvc3QuIAoKYGBge3J9CiMgc3BhY2VyYW5nZXIgb25seSBnZW5lcyB0byBkbyBPUkEgCnNwYWNlcmFuZ2VyX29ubHlfZ2VuZXMgPC0gc3BhY2VyYW5nZXJfb25seV9nZW5lX3RhYmxlICU+JQogIHVuaXF1ZSgpICU+JQogIGRwbHlyOjpwdWxsKGdlbmVfbmFtZSkKYGBgCgpgYGB7cn0Kc3BhY2VyYW5nZXJfb3JhX3Jlc3VsdHMgPC0gZW5yaWNoR08oZ2VuZSA9IHNwYWNlcmFuZ2VyX29ubHlfZ2VuZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHVuaXZlcnNlID0gYmFja2dyb3VuZF9nZW5lcywKICAgICAgICAgICAgICAgICAgICAgICAgICAga2V5VHlwZSA9ICJTWU1CT0wiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBPcmdEYiA9IG9yZy5Icy5lZy5kYiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgb250ID0gIkJQIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcEFkanVzdE1ldGhvZCA9ICJCSCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiA9IDAuMDAwMDEpCmBgYAoKCmBgYHtyfQojIGxvb2sgYXQgZ2VuZSBvbnRvbG9neSByZXN1bHRzIApzcGFjZXJhbmdlcl9nb19yZXN1bHRzIDwtIHNwYWNlcmFuZ2VyX29yYV9yZXN1bHRzQHJlc3VsdCAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgZHBseXI6OmZpbHRlcihwLmFkanVzdCA8IDAuMikKc3BhY2VyYW5nZXJfZ29fcmVzdWx0cwpgYGAKCgpgYGB7cn0KIyBzYXZlIHNwZSBsaXN0CnNwZV9maWxlIDwtIGZpbGUucGF0aChyZXN1bHRzX2RpciwgImFsbF9zcGVfbGlzdF9lbnNlbWJsX3YxMDQucmRzIikKcmVhZHI6OndyaXRlX3JkcyhhbGxfc3BlX2xpc3QsIHNwZV9maWxlKQpgYGAKCgojIyBDb25jbHVkaW5nIHRob3VnaHRzIAoKLSBBbGV2aW4tZnJ5IGFuZCBTcGFjZXJhbmdlciBvbmx5IHJlc3VsdCBpbiBzaW1pbGFyIGRpc3RyaWJ1dGlvbnMgb2YgVU1JL3Nwb3QgYW5kIGdlbmVzL3Nwb3QsIGFsdGhvdWdoIGl0IGlzIG5vdCBxdWl0ZSBhcyBuaWNlIGFzIHRoZSBvdmVybGF5IHlvdSBzZWUgd2l0aCBzaW5nbGUtY2VsbCBsaWJyYXJpZXMuIAotIFVzaW5nIGFsZXZpbi1mcnktdW5maWx0ZXJlZCByZXN1bHRzIGluIGlkZW50aWZpY2F0aW9uIG9mIHRoZSBzYW1lIHNwb3RzIHRoYXQgYXJlIGlkZW50aWZpZWQgaW4gU3BhY2VyYW5nZXIuIAotIEl0IGFwcGVhcnMgdGhhdCBTcGFjZXJhbmdlciBoYXMgc2xpZ2h0bHkgaGlnaGVyIFVNSS9jZWxsIGFuZCBnZW5lcyBkZXRlY3RlZC9jZWxsLiAKLSBCb3RoIHRvb2xzIGFsc28gc2hvdyBoaWdoIGNvcnJlbGF0aW9uIGluIG1lYW4gZ2VuZSBleHByZXNzaW9uIGFuZCBoaWdoIG92ZXJsYXAgaW4gdGhlIGdlbmVzIHRoYXQgYXJlIGRldGVjdGVkLCBhbHRob3VnaCB0aGVyZSBpcyBhbiBpbmNyZWFzZSBpbiBnZW5lIGV4cHJlc3Npb24gaW4gU3BhY2VyYW5nZXIgYWxvbmUuIAoKIyMgU2Vzc2lvbiBJbmZvCgpgYGB7cn0Kc2Vzc2lvbmluZm86OnNlc3Npb25faW5mbygpCmBgYAoK
>>>>>>> benchmarking-spatial-fry-unfiltered